Preprocesing data

Przekształcanie i czyszczenie danych


Ten dokument opisuje sposoby, możliwości i techniki porządkowania i czyszczenia danych z użyciem biblioteki (Pandas) i (Polars) python.

Mam nadzieję, że będzie to dla Ciebie przydatne!
Miłej lektury!
Author

Piotr Dłubak

Published

February 22, 2025

I. Wprowadzenie

1. Zakres Badania

Analiza danych:

  1. Badana populacja: Klienci sklepów spożywczych

  2. Rodzaj badania: Ankieta on-line dostępna dla wszystkich użytkowników na terenie Polski

  3. Metoda analizy: EDA (“Exploratory Data Analysis”) - analiza deskrypcyjna

  4. Parametry badania:

    Zmienne ilościowe:

    • wiek
    • liczba osób w rodzinie
    • dochody
    • wydatki

    Zmienne jakościowe:

    • płeć
    • wykształcenie
    • preferowany typ sklepu
    • preferowana marka sklepu
    • preferowanay towar
    • czynnik zakupowy
    • rodzaj promocji
    • miasto

Wyniki przeprowadzonej analizy pokazały, że dla współczesnego konsumenta decyzja o zakupie produktu nie jest motywowana wyłącznie chęcią zaspokojenia określonych potrzeb, ale w znacznym stopniu determinuje ją potrzeba demonstracji przekonań, statusu społeczno-ekonomicznego i stylu życia. Nowoczesny model konsumpcji niesie określone konsekwencje dla praktyki działań marketingowych.

Badanie ankietowe dotyczyło postaw i zachowań respondentów w trakcie dokonywania zakupów produktów żywnościowych. W tym celu został skonstruowany kwestionariusz ankiety.

Cechy, takie jak wiek, płeć, wykształcenie, preferowany typ sklepu, dochody netto, preferowana marka sklepu, preferowany towar, preferowany rodzaj promocji oraz czynnik zakupowy, mogą wpływać na wysokość zakupów żywnościowych.

2. Wymagania dotyczące danych:

Tip

Zdefiniowenie zakresu analizy (zmienne, obserwacje, zakresy dziedzinowe)

  1. Zmienne ilościowe:
    • wiek: 13-105 lat
    • liczba osób w rodzinie: 1-20
    • dochody: >0
    • wydatki: >0
  2. Zmienne jakościowe:
    • płeć: “mężczyzna”, “kobieta”
    • wykształcenie: “podstawowe”, “zawodowe”, “średnie”, “wyższe”
    • preferowany typ sklepu: “bazarek”, “osiedlowy”, “supermarket”, “galeria”
    • preferowana marka sklepu: dowolne
    • preferowany towar: dowolne
    • czynnik zakupowy: dowolne
    • rodzaj promocji: “sugestia kasjera”, “gazetka”, “reklama RTV”, “sms”, “e-mail”, “karta”, “aplikacja”, “nie korzystam”
    • miasto: dowolne

3. Co to jest Preprocesing danych?

Tip

Preprocessing danych – co to jest i dlaczego jest ważny?

Preprocessing danych to proces przygotowania surowych danych do analizy. Obejmuje on różne techniki i metody, które mają na celu przekształcenie, oczyszczenie i uporządkowanie danych, aby były one gotowe do dalszej analizy. Preprocessing danych jest kluczowy, ponieważ surowe dane często zawierają błędy, braki, duplikaty oraz inne nieprawidłowości, które mogą wpłynąć na wyniki analizy. Poprawne przygotowanie danych pozwala na uzyskanie bardziej wiarygodnych i dokładnych wyników, co jest niezbędne w procesie podejmowania decyzji opartych na danych.

  • Etapy preprocessingu danych:
    1. Pozyskanie danych: Zbieranie danych z różnych źródeł, takich jak bazy danych, pliki CSV, API, itp.
    2. Załadowanie danych: Importowanie danych do odpowiednich struktur danych, np. DataFrame w Pandas.
    3. Uporządkowanie danych: Identyfikacja i rozwiązanie problemów związanych z nieuporządkowanymi danymi, takich jak podzielone obserwacje, nieprawidłowe nagłówki kolumn, itp.
    4. Czyszczenie danych: Usuwanie błędów, braków, duplikatów oraz innych nieprawidłowości w danych.
    5. Wzbogacanie danych: Tworzenie nowych zmiennych oraz redukcja liczby poziomów zmiennych kategorycznych.
    6. Analiza danych: Przeprowadzanie analizy eksploracyjnej (EDA) w celu zidentyfikowania kluczowych cech, trendów oraz relacji w danych.
  • Zalety preprocessingu danych:
    • Poprawa jakości danych: Usunięcie błędów, brakujących wartości i anomalii, co zapobiega błędnym wnioskom.
    • Zwiększenie wydajności modeli ML: Dobrze przygotowane dane poprawiają skuteczność algorytmów uczenia maszynowego.
    • Redukcja szumu: Minimalizuje wpływ nieistotnych lub błędnych informacji.
    • Lepsza interpretacja wyników: Przejrzyste i dobrze przygotowane dane ułatwiają analizę i wyciąganie wniosków.
    • Efektywność obliczeniowa: Optymalizacja struktury danych zmniejsza czas i zasoby potrzebne do analizy.

4. Co to jest analiza EDA?”

Tip
  • Analiza deskrypcyjna to proces badania, opisywania i interpretacji danych w celu uzyskania wglądu i zrozumienia ich cech, wzorców i związków. Jest to technika często stosowana w dziedzinach naukowych, badań społecznych, statystyce, lingwistyce i wielu innych dziedzinach. Głównym celem analizy deskrypcyjnej jest opisanie i podsumowanie danych w sposób, który ujawnia istotne informacje. Może obejmować takie elementy jak obliczanie średnich, median, odchyleń standardowych, minimalnych i maksymalnych wartości, oraz prezentowanie danych w postaci tabel, wykresów lub grafów.
  • Analiza deskrypcyjna umożliwia identyfikację kluczowych cech, trendów, anomalii i relacji w danych. Może również pomóc w odkrywaniu wzorców, porównywaniu grup lub kategorii danych oraz wyprowadzaniu wniosków na podstawie zebranych informacji. Głównym celem EDA jest zapewnienie wglądu w dane jeszcze przed sformułowaniem jakichkolwiek założeń. Pomaga identyfikować oczywiste błędy, lepiej pojmować wzorce występujące w obrębie danych, wykrywać wartości odstające i anomalie, a także odnajdywać interesujące relacje między zmiennymi. Po przeprowadzeniu analizy EDA i uzyskaniu istotnych spostrzeżeń wciąż można wykorzystać tę metodę do bardziej zaawansowanej analizy danych lub modelowania, w tym na potrzeby uczenia maszynowego.

4. Czym są dane uprządkowane?”

Czym są dane uprządkowane?
  • Koncepcja danych uporządkowanych (tidy data) została opisana przez Hadleya Wickhama i stanowi zbiór zasad, które mają na celu ujednolicenie struktury danych, aby ułatwić ich analizę, manipulację i wizualizację.
  • Kluczowym założeniem tidy data jest to, że dane powinny być przedstawione w sposób „czysty”, co eliminuje niejednoznaczności i pozwala stosować zunifikowane podejście do operacji na danych.
  • Zasady tidy data służą do tworzenia struktury danych, która maksymalizuje efektywność procesów analitycznych, pozwala na automatyzację rutynowych zadań oraz minimalizuje ryzyko popełnienia błędów przy dalszej obróbce i modelowaniu danych.

5. Zasady danych uporządkowanych

Tip

Podstawowe zasady: - Każda zmienna tworzy kolumnę. - Każda obserwacja tworzy wiersz. - Każdy rodzaj jednostki tworzy tabelę.

Korzyści uporządkowania danych: - Ułatwia przekształcanie, analizę i wizualizację. - Zapewnia ustrukturyzowany i ustandaryzowany sposób organizacji danych. - Łączy fizyczny układ danych z ich semantyką.

Struktura ramki danych: - Kolumny reprezentują nazwy zmiennych (etykiety). - Wiersze zawierają wartości obserwacji.

Semantyka danych: - Zbiór danych to zbiór wartości: - Liczby → dla zmiennych ilościowych. - Ciągi znaków → dla zmiennych jakościowych. - Każda wartość należy do konkretnej: - Zmiennej (kolumna). - Obserwacji (wiersz).

6. Źródła danych

Tip

Dane można pozyskiwać z różnych źródeł w zależności od celu analizy:

  1. Dane publiczne i otwarte
  • Bazy danych rządowe – np. Eurostat, GUS, UCI Machine Learning Repository, Open Data Portal.
  • Dane naukowe – Kaggle Datasets, Google Dataset Search.
  • APIs – np. Twitter API, OpenWeatherMap, Google Maps API.
  1. Wewnętrzne źródła danych
  • Bazy relacyjne (SQL, NoSQL) – np. PostgreSQL, MongoDB.
  • Dane logów i telemetryczne – np. pliki JSON, Parquet, Apache Kafka.
  • Dane biznesowe – CRM, ERP, systemy księgowe.
  1. Dane generowane przez użytkowników
  • Ankiety i formularze – Google Forms, SurveyMonkey.
  • Social media – Facebook Graph API, YouTube API.
    • Systemy IoT – sensory, urządzenia smart.

Pobieranie danych

Dane można pobierać na kilka sposobów:

  1. Pobieranie ręczne
  • Pobranie plików CSV, Excel, JSON, XML.
  • Eksport z baz danych.
  1. Web Scraping
  • Biblioteki Python: BeautifulSoup, Scrapy, Selenium.
  • API REST: requests, urllib.
  1. Automatyczna ekstrakcja i strumieniowanie danych
  • Apache Kafka, Spark Streaming – do przetwarzania danych w czasie rzeczywistym.
  • ETL (Extract, Transform, Load) – np. Airflow, Talend, dbt.

7. Czym jest Czyszczenie danych?

Tip

Odpowiedzią na ww. nieprawidłowości będą odpowiednie oczyszczenie danych z błędów. 1. Zdefiniowanie czystych danych: - Dane muszą być dokładne. - Kompletne. - Spójne. - Ważne. - Aktualne. - Bez duplikatów. - Jednolite.

  1. Rozpoznanie „brudnych” danych:

    Możliwe oznaki zanieczyszczonych danych:

    • Różna pisownia wariantów zmiennej tekstowej
    • Zbędne myślniki.
    • Litery drukowane.
    • Wartości niezgodne z rzeczywistością.
    • Zbędne odstępy.
    • Literówki.
    • Dane niezgodne z wiedzą dziedzinową.
    • Dane niezgodne ze zdrowym rozsądkiem.
    • Wartości liczbowe we wartościach zmiennej kategorycznej, gdzie powinna być wartość tekstowa.
    • Wartości tekstowe w zmiennych ilościowych.
    • Duplikaty.
    • Myślniki zamiast wartości.
    • Zera zamiast wartości.
    • Wartości brakujące-puste.
    • Różne jednostki jednej zmiennej.
  2. Zalety czyszczenia danych:

    • Poprawa jakości danych: Usunięcie błędów, brakujących wartości i anomalii, co zapobiega błędnym wnioskom.
    • Zwiększenie wydajności modeli ML: Dobrze przygotowane dane poprawiają skuteczność algorytmów uczenia maszynowego.
    • Redukcja szumu: Minimalizuje wpływ nieistotnych lub błędnych informacji.
    • Lepsza interpretacja wyników: Przejrzyste i dobrze przygotowane dane ułatwiają analizę i wyciąganie wniosków.
    • Efektywność obliczeniowa: Optymalizacja struktury danych zmniejsza czas i zasoby potrzebne do analizy.

Odpowiedzią na ww. nieprawidłowości będzie odpowiednie oczyszczenie danych z błędów.

Code
# ```{mermaid}
# flowchart LR
#   A{Preprocesing danych} --> B(Pozyskanie danych)
#   A --> C(Porządkowanie danych)
#   A --> D(Czyszczenie danych)
#   B --> E[Źródła danych]
#   E --> H(Dane publiczne i otwarte)
#   E --> I(Wewnętrzne źródła danych)
#   E --> J(Dane generowane)
#   B --> F[Pobieranie danych]
#   F --> K(Pobieranie ręczne)
#   F --> L(Web Scraping)
#   F --> M(Strumieniowanie danych)
#   C --> N(Identyfikcja  nieprawidłowości)
#   C --> O(Rodzaje nieprawidłowosci)
#   O --> OA(Podzielone obserwacje tych samych zmiennych pomiędzy wiele tabel)
#   O --> OB(Przechowywanie w rożnych wierszach jednej kolumny wartości wielu zmiennych)
#   O -->OC(Używanie jako nagłówków kolumn wartości zamiast nazw zmiennych)
#   O -->OD(Podzielone wartości jednej zmiennej pomiędzy kilka kolumn)
#   O -->OE(Przechowywanie w jednej kolumnie połączonych wartości wielu zmiennych)
#   O -->OF(Zebrane dane podzielone pomiędzy wiele plików)
#   O -->OG(Zebrane dane w plikach zostały zapisane w różnych formatach)
#   C --> P(Dokonanie przekształceń i zmian w tabelach danych)
#   D --> S(ddd)
#   D --> T(eee)
#   D --> U(hhh)
# ```

1. Przygotowanie danych

1.1. Pozyskanie danych

  • Dane pozyskano z przeprowadzonego badania ankietowego, które dotyczyło postaw i zachowań respondentów w trakcie dokonywania zakupów produktów żywnościowych. W tym celu został skonstruowany kwestionariusz ankiety. Dane ankietowe zostały zebrane w formie plików, które następnie załadowano do analizy. Pliki te zawierają informacje o respondentach, takie jak wiek, płeć, wykształcenie, preferencje zakupowe oraz czynniki wpływające na decyzje zakupowe.

  • Szczegółowe dane zostały zapisane w następujących plikach:

    1. ankieta_01a.xlsx
    2. ankieta_01b.xlsx
    3. ankieta_02.csv
    4. miasta.json

1.2. Pobranie i załadowanie danych

Dane te zostały załadowane do odpowiednich DataFrame’ów:

Plik DataFrame Opis
ankieta_01a.xlsx ankieta_01a Zawiera podstawowe informacje o respondentach.
ankieta_01b.xlsx ankieta_01b Zawiera dodatkowe informacje o respondentach.
ankieta_02.csv ankieta_02 Zawiera dane dotyczące dochodów i wydatków respondentów.
miasta.json miasta Zawiera informacje o miastach, z których pochodzą respondenci.

1.3. Podgląd tabel

Code
import warnings
import pandas as pd
import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
import statsmodels.api as sm
import random
from scipy.stats import zscore
import re
import math
warnings.filterwarnings("ignore")
from IPython.display import display, Markdown
import polars as pl
pd.set_option('display.max_colwidth', None)



ankieta_01a = pd.read_excel('ankieta_01a.xlsx')
ankieta_01b = pd.read_excel('ankieta_01b.xlsx')
ankieta_02 = pd.read_csv('ankieta_02.csv', skiprows=1, skipfooter=1, delimiter=';',engine='python')
miasta = pd.read_json('miasta.json')


display(Markdown("**Informacja o tabelach:**"))
def display_table_info(tables):
    info = []
    for name, df in tables.items():
        info.append({
            'Nazwa': name,
            'Liczba kolumn': df.shape[1],
            'Liczba wierszy': df.shape[0]
        })
    return pd.DataFrame(info)

tables = {
    'ankieta_01a': ankieta_01a,
    'ankieta_01b': ankieta_01b,
    'ankieta_02': ankieta_02,
    'miasta': miasta
}


def unique_values_summary(tables):
    summary = []
    for table_name, df in tables.items():
        for col in df.columns:
            unique_values = df[col].unique()
            summary.append({
                'Nazwa tabeli': table_name,
                'Nazwa kolumny': col,
                'Liczba unikatowych wartości': len(unique_values),
                'Lista unikatowych wartości': unique_values
            })
    return pd.DataFrame(summary)

unique_summary_df = unique_values_summary(tables)
display(unique_summary_df)





table_info = display_table_info(tables)
display(table_info)


def display_dataframe_with_title(df, title):
    display(Markdown(f"#### {title}"))
    display(df.head())

print()
#
display(Markdown("**WYŚWIETLENIE ZAWARTOŚCI TABEL - 5 pierwszych wierszy**"))
print()
display_dataframe_with_title(ankieta_01a, "Tabela: ankieta_01a")
display_dataframe_with_title(ankieta_01b, "Tabela: ankieta_01b")
display_dataframe_with_title(ankieta_02, "Tabela: ankieta_02")
display_dataframe_with_title(miasta, "Tabela: miasta")

Informacja o tabelach:

Nazwa tabeli Nazwa kolumny Liczba unikatowych wartości Lista unikatowych wartości
0 ankieta_01a id. 59 [R_001, R_002, R_003, R_004, R_005, R_006, R_007, R_008, R_009, R_010, R_011, R_012, R_013, R_014, R_015, R_016, R_017, R_018, R_019, R_020, R_021, R_022, R_023, R_024, R_025, R_026, R_027, R_028, R_029, R_030, R_031, R_032, R_033, R_034, R_035, R_036, R_037, R_038, R_039, R_040, R_041, R_042, R_043, R_044, R_045, R_046, R_047, R_048, R_049, R_050, R_051, R_052, R_053, R_054, R_055, R_056, R_057, R_058, R_059]
1 ankieta_01a kod_miasta 39 [M_058, M_050, M_069, M_059, M_057, M_001, M_021, M_016, M_053, M_033, M_027, M_043, M_032, M_037, M_010, M_041, M_071, M_002, M_080, M_017, M_039, M_062, M_008, M_054, M_026, M_036, M_029, M_068, M_074, M_047, M_052, M_023, M_013, M_079, M_064, M_055, M_009, M_015, M_040]
2 ankieta_01a wiek|liczba osób w rodzinie 52 [30|2, 16|1, 49|1, 67 lat|1, 38|5, 28|1, 158|5, 50|4, 75|4, 59|1, 24|3, 33 lata|5, 64|4, 69|1, 63|4, 45|1, 74|5, 74|3, 28|4, 42|3, 68|1, 35|5, 33|2, 23|5, 63|1, 18|1, 66|1, 64|3, 71|4, 20|2, 58|5, 69|5, 35|1, 61|5, 74|1, 69|30, 27|1, 71|3, 28|2, 62|3, 26|3, 51|4, 28|3, 44|5, 37|5, 21|3, 44|3, 56|2, 41|5, 69|2, 47|4, 26|5]
3 ankieta_01a m_wykształcenie 7 [podstawowe, podstawowe., zawodowe, średn, średnie, wyższe, nan]
4 ankieta_01a k_wykształcenie 5 [nan, podstawowe, zawodowe, śred5nie, średnie]
5 ankieta_01a PREFEROWANY TYP SKLEPU 4 [BAZAREK, OSIEDLOWY, SUPERMARKET, GALERIA]
6 ankieta_01a preferowana marka sklepu 13 [POLOMARKET, NETTO, BIEDRONKA, LIDL, DINO, KAUFLAND, ALDI, SPOŁEM, CARREFOUR, INTERMACHE, GROSZEK, ŻABKA, JEŻYK]
7 ankieta_01a preferowanay towar 8 [Napoje 5, Owoce i warzywa, Produkty piekarnicze, Mięso i wędliny, Produkty zbożowe, Napoje, słodycze, Produkty mleczne]
8 ankieta_01a preferowany rodzaj promocji 9 [sugestia, gazetka, reklama, karta, sms, email, 2gazetka, aplikacja, nie]
9 ankieta_01a preferowany rodzaj promocji.1 4 [kasjera, nan, RTV, korzystam]
10 ankieta_01a czynnik zakupowy 6 [cena, dostępność, jakość, marka, skład, lokalność]
11 ankieta_01b id. 66 [R_060, R_061, R_062, R_063, R_064, R_065, R_066, R_067, R_068, R_069, R_070, R_071, R_072, R_073, R_074, R_075, R_076, R_077, R_078, R_079, R_080, R_081, R_082, R_083, R_084, R_085, R_086, R_087, R_088, R_089, R_090, R_091, R_092, R_093, R_094, R_095, R_096, R_097, R_098, R_099, R_100, R_101, R_102, R_103, R_104, R_105, R_106, R_107, R_108, R_109, R_110, R_111, R_112, R_113, R_114, R_115, R_116, R_117, R_118, R_119, R_120, R_121, R_122, R_123, R_124, R_125]
12 ankieta_01b kod_miasta 46 [M_048, M_014, M_062, M_053, M_041, M_059, M_074, M_049, M_075, M_011, M_058, M_004, M_001, M_078, M_023, M_008, M_038, M_024, M_070, M_057, M_047, M_080, M_007, M_042, M_015, M_066, M_044, M_036, M_009, M_016, M_012, M_027, M_037, M_068, M_006, M_043, M_045, M_010, M_069, M_071, M_029, M_030, M_017, M_003, M_018, M_035]
13 ankieta_01b wiek|liczba osób w rodzinie 58 [58|2, 46|4, 16|4, 28|2, 55|5, 37|3, 35|1, 43|5, 48|4, 25|2, 39|3, 26|2, 69|2, 44|5, 71|2, 30|4, 59|3, 72|5, 46|1, 54|5, 68|2, 16|2, 39|1, 31|4, 44|1, 49|3, 63|1, 70|3, 25|1, 53|2, 58|5, 75|4, 21|2, 46|5, 78|4, 66|4, 64|5, 74|5, 66|3, 50|2, 77|5, 44|2, 61|5, 45|5, 31|1, 65|4, 53|1, 53|5, 55|1, 33|5, 21|5, 41|2, 63|2, 29|4, 56|5, 18|4, 28|4, 29|2]
14 ankieta_01b m_wykształcenie 1 [nan]
15 ankieta_01b k_wykształcenie 6 [średnie, srednie, średnie?, wyższe, wyzsze, wyższe,]
16 ankieta_01b PREFEROWANY TYP SKLEPU 7 [SUPERMARKET, SUPER, OSIEDLOWY, BAZAR, BAZAREK, GALERIA, nan]
17 ankieta_01b preferowana marka sklepu 11 [LIDL, LEWIATAN, ŻABKA, POLOMARKET, ALDI, BIEDRONKA, NETTO, CARREFOUR, KAUFLAND, DINO, nan]
18 ankieta_01b preferowanay towar 7 [Mięso i wędliny, Produkty zbożowe, Produkty mleczne, Produkty piekarnicze, Owoce i warzywa, Napoje, słodycze]
19 ankieta_01b preferowany rodzaj promocji 6 [gazetka, karta, reklama, nie, sugestia, nan]
20 ankieta_01b preferowany rodzaj promocji.1 4 [nan, RTV, korzystam, kasjera]
21 ankieta_01b czynnik zakupowy 9 [cena, jakość, marka, dostępność, lokalność, opinie, opakowanie, preferencje kulinarne, skład]
22 ankieta_02 id. 125 [R_001, R_002, R_003, R_004, R_005, R_006, R_007, R_008, R_009, R_010, R_011, R_012, R_013, R_014, R_015, R_016, R_017, R_018, R_019, R_020, R_021, R_022, R_023, R_024, R_025, R_026, R_027, R_028, R_029, R_030, R_031, R_032, R_033, R_034, R_035, R_036, R_037, R_038, R_039, R_040, R_041, R_042, R_043, R_044, R_045, R_046, R_047, R_048, R_049, R_050, R_051, R_052, R_053, R_054, R_055, R_056, R_057, R_058, R_059, R_060, R_061, R_062, R_063, R_064, R_065, R_066, R_067, R_068, R_069, R_070, R_071, R_072, R_073, R_074, R_075, R_076, R_077, R_078, R_079, R_080, R_081, R_082, R_083, R_084, R_085, R_086, R_087, R_088, R_089, R_090, R_091, R_092, R_093, R_094, R_095, R_096, R_097, R_098, R_099, R_100, ...]
23 ankieta_02 miara 2 [dochody roczne, wydatki_na_żywność_mc]
24 ankieta_02 wartość 130 [54, 750, 26tys, 2300, 50, 430, 45, 507, 28, 0, -556, 29 zł, 563, 42, 650, 30, 590, -31, 591, 31, 607, 32, 350, 656, 667, 99, 669, 34, 669 PLN, 682, 687, 35, 690, 691, 705, 712, 713, 731, 740, 742, 746, 748, 753, 755, 757, 38, 761, 762, 769, 771, 778, 39, 783, 784, 786, 792, 801, 40, 804, 808, 41, 815, 816, 823, 826, 829, 33, 831, 833, 844, 43, 845, 846, 855, 863, 865, 875, 44, 876, 878, 881, 884, 885, 891, 896, 898, 901, 910, 928, 46, 930, 931, 936, 47, 939, 941, 943, 945, 947, 48, ...]
25 miasta kod_miasta 90 [M_001, M_002, M_003, M_004, M_005, M_006, M_007, M_008, M_009, M_010, M_011, M_012, M_013, M_014, M_015, M_016, M_017, M_018, M_019, M_020, M_021, M_022, M_023, M_024, M_025, M_026, M_027, M_028, M_029, M_030, M_031, M_032, M_033, M_034, M_035, M_036, M_037, M_038, M_039, M_040, M_041, M_042, M_043, M_044, M_045, M_046, M_047, M_048, M_049, M_050, M_051, M_052, M_053, M_054, M_055, M_056, M_057, M_058, M_059, M_060, M_061, M_062, M_063, M_064, M_065, M_066, M_067, M_068, M_069, M_070, M_071, M_072, M_073, M_074, M_075, M_076, M_077, M_078, M_079, M_080, M_081, M_082, M_083, M_084, M_085, M_086, M_087, M_088, M_089, M_090]
26 miasta Miasto 89 [Warszawa, Kraków, Łódź, Wrocław, Poznań, Gdańsk, Szczecin, Bydgoszcz, Lublin, Białystok, Katowice, Gdynia, Częstochowa, Radom, Sosnowiec, Toruń, Kielce, Rzeszów, Gliwice, Zabrze, Olsztyn, Bielsko-Biała, Bytom, Ruda Śląska, Rybnik, Tychy, Gorzów Wielkopolski, Opole, Elbląg, Płock, Wałbrzych, Zielona Góra, Włocławek, Tarnów, Chorzów, Koszalin, Kalisz, Legnica, Grudziądz, Jaworzno, Słupsk, JastrzębieZdrój, Nowy Sącz, Jelenia Góra, Gniezno, Piotrków Trybunalski, Konin, Radomsko, Suwałki, Piła, Inowrocław, Lubin, Ostrów Wielkopolski, Siedlce, Skierniewice, Ostrowiec Świętokrzyski, Głogów, Stargard, Chełm, Leszno, Zamość, Żory, Tarnowskie Góry, Bełchatów, Ełk, Przemyśl, Tomaszów Mazowiecki, Świdnica, Piekary Śląskie, Tczew, Wałcz, Będzin, Biała Podlaska, Ropczyce, Ciechanów, Legionowo, Świnoujście, Zduńska Wola, Pruszków, Sułkowice, Piaseczno, Włodawa, Biała Rawska, Ostrzeszów, Sulejów, Nowe Miasto Lubawskie, Śrem, Ostroróg, Sieniawa]
27 miasta Liczba ludności 90 [1790658, 779115, 684113, 641607, 538633, 470907, 401907, 355981, 339784, 296549, 291553, 246787, 231505, 215909, 205196, 202386, 195563, 194450, 180972, 172686, 170904, 170516, 166480, 139442, 138133, 131349, 124581, 121592, 119144, 119114, 118525, 118454, 115561, 112956, 110428, 107469, 103721, 101331, 96243, 94731, 93460, 91305, 84270, 81542, 70835, 70078, 76279, 74035, 49898, 69442, 74618, 75535, 78937, 67825, 77314, 49695, 69715, 68170, 71464, 63010, 63058, 65902, 60962, 61929, 61722, 60252, 65202, 62944, 60281, 58926, 59978, 52445, 58705, 57957, 57275, 47828, 54957, 41575, 43719, 57376, 45206, 51042, 23433, 20163, 15746, 13876, 10239, 7730, 5288, 3287]
Nazwa Liczba kolumn Liczba wierszy
0 ankieta_01a 11 62
1 ankieta_01b 11 66
2 ankieta_02 3 250
3 miasta 3 90

WYŚWIETLENIE ZAWARTOŚCI TABEL - 5 pierwszych wierszy

Tabela: ankieta_01a

id. kod_miasta wiek|liczba osób w rodzinie m_wykształcenie k_wykształcenie PREFEROWANY TYP SKLEPU preferowana marka sklepu preferowanay towar preferowany rodzaj promocji preferowany rodzaj promocji.1 czynnik zakupowy
0 R_001 M_058 30|2 podstawowe NaN BAZAREK POLOMARKET Napoje 5 sugestia kasjera cena
1 R_002 M_050 16|1 podstawowe NaN BAZAREK NETTO Owoce i warzywa gazetka NaN dostępność
2 R_003 M_069 49|1 podstawowe. NaN BAZAREK BIEDRONKA Produkty piekarnicze reklama RTV jakość
3 R_004 M_059 67 lat|1 podstawowe NaN OSIEDLOWY LIDL Produkty piekarnicze gazetka NaN jakość
4 R_005 M_057 38|5 zawodowe NaN OSIEDLOWY LIDL Mięso i wędliny gazetka NaN jakość

Tabela: ankieta_01b

id. kod_miasta wiek|liczba osób w rodzinie m_wykształcenie k_wykształcenie PREFEROWANY TYP SKLEPU preferowana marka sklepu preferowanay towar preferowany rodzaj promocji preferowany rodzaj promocji.1 czynnik zakupowy
0 R_060 M_048 58|2 NaN średnie SUPERMARKET LIDL Mięso i wędliny gazetka NaN cena
1 R_061 M_014 46|4 NaN srednie SUPERMARKET LEWIATAN Produkty zbożowe gazetka NaN jakość
2 R_062 M_062 16|4 NaN średnie SUPERMARKET ŻABKA Produkty zbożowe karta NaN jakość
3 R_063 M_053 28|2 NaN średnie SUPER ŻABKA Produkty mleczne reklama RTV jakość
4 R_064 M_041 55|5 NaN średnie SUPERMARKET POLOMARKET Produkty mleczne gazetka NaN cena

Tabela: ankieta_02

id. miara wartość
0 R_001 dochody roczne 54
1 R_001 wydatki_na_żywność_mc 750
2 R_002 dochody roczne 26tys
3 R_002 wydatki_na_żywność_mc 2300
4 R_003 dochody roczne 50

Tabela: miasta

kod_miasta Miasto Liczba ludności
0 M_001 Warszawa 1790658
1 M_002 Kraków 779115
2 M_003 Łódź 684113
3 M_004 Wrocław 641607
4 M_005 Poznań 538633

2. Uporządkowanie danych

Code

nieprawidłowosći = pd.DataFrame({
        "Rodzaj nieprawidłowości": [
            "Podzielone obserwacje tych samych zmiennych pomiędzy wiele tabel",
            "Przechowywanie w rożnych wierszach jednej kolumny wartości wielu zmiennych",
            "Używanie jako nagłówków kolumn wartości zamiast nazw zmiennych",
            "Podzielone wartości jednej zmiennej pomiędzy kilka kolumn",
            "Przechowywanie w jednej kolumnie połączonych wartości wielu zmiennych",
            "Zebrane dane podzielone pomiędzy wiele plików",
            "Zebrane dane w plikach zostały zapisane w różnych formatach"
        ],
        "Nazwa pliku": [
            "ankieta_01a, ankieta_01b",
            "ankieta_02",
            "ankieta_01a, ankieta_01b",
            "ankieta_01a, ankieta_01b",
            "ankieta_01a, ankieta_01b",
            "ankieta_01a, ankieta_01b, ankieta_02, miasta",
            "ankieta_01a, ankieta_01b, ankieta_02, miasta"
        ],
        "Sposób rozwiązania": [
            "Połączenie plików ankieta_01a i ankieta_01b w jeden plik ankieta_01",
            "Przekształcenie kolumnowo-wierszowe (unpivot)",
            "Przekształcenie kolumnowo-wierszowe (pivot) i zamiana nazw kolumn",
            "Połączenie kolumn preferowany rodzaj promocji i preferowany rodzaj promocji.1",
            "Podzielenie kolumny wiek|liczba osób w rodzinie na dwie kolumny: wiek i liczba osób w rodzinie",
            "Połączenie tabel ankieta_01, ankieta_02 i miasta w jedną tabelę baza",
            "Konwersja wszystkich plików do formatu pandas DataFrame"
        ],

        "opis funkcji w python użytej w kodzie":[
            "pd.concat() - Łączy dwa lub więcej DataFrame wzdłuż określonej osi.",
            "unstack() - Przekształca poziom etykiet indeksu (koniecznie hierarchicznego).",
            "stack() - Przekształca kolumny DataFrame w indeks.",
            "str.cat() - Łączy ciągi w Series/Index z podanym separatorem.",
            "str.split() - Dzieli ciągi wokół podanego separatora/delimitera.",
            "pd.merge() - Łączy obiekty DataFrame lub Series z bazodanowym stylem join.",
            "pd.read_*() - Wczytuje dane z różnych formatów plików do DataFrame."
        ]
    })

Identyfikacja oznak danych nieuporządkowych:

Code

display(nieprawidłowosći)
Rodzaj nieprawidłowości Nazwa pliku Sposób rozwiązania opis funkcji w python użytej w kodzie
0 Podzielone obserwacje tych samych zmiennych pomiędzy wiele tabel ankieta_01a, ankieta_01b Połączenie plików ankieta_01a i ankieta_01b w jeden plik ankieta_01 pd.concat() - Łączy dwa lub więcej DataFrame wzdłuż określonej osi.
1 Przechowywanie w rożnych wierszach jednej kolumny wartości wielu zmiennych ankieta_02 Przekształcenie kolumnowo-wierszowe (unpivot) unstack() - Przekształca poziom etykiet indeksu (koniecznie hierarchicznego).
2 Używanie jako nagłówków kolumn wartości zamiast nazw zmiennych ankieta_01a, ankieta_01b Przekształcenie kolumnowo-wierszowe (pivot) i zamiana nazw kolumn stack() - Przekształca kolumny DataFrame w indeks.
3 Podzielone wartości jednej zmiennej pomiędzy kilka kolumn ankieta_01a, ankieta_01b Połączenie kolumn preferowany rodzaj promocji i preferowany rodzaj promocji.1 str.cat() - Łączy ciągi w Series/Index z podanym separatorem.
4 Przechowywanie w jednej kolumnie połączonych wartości wielu zmiennych ankieta_01a, ankieta_01b Podzielenie kolumny wiek|liczba osób w rodzinie na dwie kolumny: wiek i liczba osób w rodzinie str.split() - Dzieli ciągi wokół podanego separatora/delimitera.
5 Zebrane dane podzielone pomiędzy wiele plików ankieta_01a, ankieta_01b, ankieta_02, miasta Połączenie tabel ankieta_01, ankieta_02 i miasta w jedną tabelę baza pd.merge() - Łączy obiekty DataFrame lub Series z bazodanowym stylem join.
6 Zebrane dane w plikach zostały zapisane w różnych formatach ankieta_01a, ankieta_01b, ankieta_02, miasta Konwersja wszystkich plików do formatu pandas DataFrame pd.read_*() - Wczytuje dane z różnych formatów plików do DataFrame.

2.1. Dane zostały pozielona na dwa pliki

Powyższe niezgodności i nieprawidłowości w danych należy przekształcić, połączyć. W rezultacie powinnismy otrzymać jeden plik danych zawiejający dane,które będą w następnym etapie stnowić bazę do procesu czyszczenia danych

Code
import pandas as pd
from IPython.display import display_html

display(Markdown(f"#### Niezgodnośc nr 1. Dane z ankiety [ankieta_01] zostały pozielona na dwa pliki: ankieta_01a i ankieta_01b"))
display(Markdown(f"* Rozwiązaniem jest połączenie tych plików w jeden pod nazwą (ankieta_01)"))
display(Markdown(f"* Przed połączeniem należy sprawdzić zgodnośc tabel w zakresie kolumn - dane są zgodne"))

if list(ankieta_01a.columns) == list(ankieta_01b.columns):
    display(Markdown(f"*    Liczba wierszy w ankieta_01a:  {len(ankieta_01a)}"))
    display(Markdown(f"*    Liczba wierszy w ankieta_01b:  {len(ankieta_01b)}"))
else:
    display(Markdown(f"*Dane niezgodne"))

ankieta_01 = pd.concat([ankieta_01a, ankieta_01b], axis=0)
display(Markdown(f"* Dane z dwóch plków połączno w jeden  ankieta_01: "))
display(Markdown(f"* Suma wierszy w ankieta_01: {ankieta_01.shape[0]}"))
#display(ankieta_01)

Niezgodnośc nr 1. Dane z ankiety [ankieta_01] zostały pozielona na dwa pliki: ankieta_01a i ankieta_01b

  • Rozwiązaniem jest połączenie tych plików w jeden pod nazwą (ankieta_01)
  • Przed połączeniem należy sprawdzić zgodnośc tabel w zakresie kolumn - dane są zgodne
  • Liczba wierszy w ankieta_01a: 62
  • Liczba wierszy w ankieta_01b: 66
  • Dane z dwóch plków połączno w jeden ankieta_01:
  • Suma wierszy w ankieta_01: 128

2.2. W jednej kolumnie przechowywane są nazwy 2 różnych zmiennych

Code

#---------------------------------------------------------------------------------------------------

import pandas as pd
from IPython.display import display_html, Markdown, display

def display_side_by_side(*dfs, margin='50px', titles=None):
  
    html_str = ""
    for i, df in enumerate(dfs):
        # Jeśli lista tytułów została podana i zawiera tytuł dla danej tabeli, dodajemy go nad tabelą
        title_html = ""
        if titles is not None and i < len(titles):
            title_html = f'<h4 style="text-align:center; margin-bottom:5px;">{titles[i]}</h4>'
        # Każdy DataFrame jest owinięty w <div> z ustawionym marginesem
        html_str += (
            f'<div style="display: inline-block; margin-right: {margin}; vertical-align: top;">'
            f'{title_html}'
            f'{df.to_html(classes="dataframe", border=1)}'
            f'</div>'
        )
    display_html(html_str, raw=True)


display(Markdown("#### Niezgodność nr 2. W jednej kolumnie [miara] przechowywane są nazwy 2 różnych zmiennych, a w kolumnie [wartość] wartości odpowiadające różnym zmiennym w pliku ankieta_02"))
display(Markdown("* Rozwiązaniem jest dokonanie przekształcenia kolumnowo-wierszowego tzw. unpivot - unstack"))

ankieta_02_jeden = ankieta_02.head()
ankieta_02 = ankieta_02.set_index(['id.', 'miara']).unstack().reset_index()

# Wyświetlenie DataFrame obok siebie z tytułami nad każdą tabelą
display_side_by_side(
    ankieta_02_jeden.head(), 
    ankieta_02.head(), 
    titles=["Oryginalne dane", "Dane po transformacji"]
)

#------------------------------------------------------------------------------------------------------------------

Niezgodność nr 2. W jednej kolumnie [miara] przechowywane są nazwy 2 różnych zmiennych, a w kolumnie [wartość] wartości odpowiadające różnym zmiennym w pliku ankieta_02

  • Rozwiązaniem jest dokonanie przekształcenia kolumnowo-wierszowego tzw. unpivot - unstack

Oryginalne dane

id. miara wartość
0 R_001 dochody roczne 54
1 R_001 wydatki_na_żywność_mc 750
2 R_002 dochody roczne 26tys
3 R_002 wydatki_na_żywność_mc 2300
4 R_003 dochody roczne 50

Dane po transformacji

id. wartość
miara dochody roczne wydatki_na_żywność_mc
0 R_001 54 750
1 R_002 26tys 2300
2 R_003 50 430
3 R_004 45 507
4 R_005 28 750

2.3 Używanie jaka nagłówków kolumn wartosci zamiast nazw zmiennych

Code



display(Markdown(f"#### Niezgodnośc nr 3. Używanie jaka nagłówków kolumn wartosci zamiast nazw zmiennych - Zmienna wykształcenie i płeć zostały połączone w jednej kolumnie"))
display(Markdown(f"* Rozwiązaniem jest dokonanie przekształcenia kolumnowo-wierszowego tzw. pivot - stack"))
display()
an_01 =ankieta_01[['id.','m_wykształcenie','k_wykształcenie']]

t1=ankieta_01[['id.','m_wykształcenie','k_wykształcenie']]
t1=t1.set_index('id.').stack().reset_index()
t1.columns  = ['id.', 'typ_wykształcenia', 'wykształcenie']
t1['typ_wykształcenia'] = t1['typ_wykształcenia'].str.replace('_wykształcenie', '')
t1 = t1.rename(columns={'typ_wykształcenia': 'płeć'})
ankieta_01 = pd.merge(ankieta_01, t1, on = 'id.',how = 'left'   )
ankieta_01 = ankieta_01.drop(['m_wykształcenie','k_wykształcenie'], axis=1)




display_side_by_side(
    an_01.head(), 
    ankieta_01[['id.','płeć',   'wykształcenie']].head(), 
    titles=["Oryginalne dane", "Dane po transformacji"]
)

Niezgodnośc nr 3. Używanie jaka nagłówków kolumn wartosci zamiast nazw zmiennych - Zmienna wykształcenie i płeć zostały połączone w jednej kolumnie

  • Rozwiązaniem jest dokonanie przekształcenia kolumnowo-wierszowego tzw. pivot - stack

Oryginalne dane

id. m_wykształcenie k_wykształcenie
0 R_001 podstawowe NaN
1 R_002 podstawowe NaN
2 R_003 podstawowe. NaN
3 R_004 podstawowe NaN
4 R_005 zawodowe NaN

Dane po transformacji

id. płeć wykształcenie
0 R_001 m podstawowe
1 R_002 m podstawowe
2 R_003 m podstawowe.
3 R_004 m podstawowe
4 R_005 m zawodowe

2.4 Podzielone wartości jednej zmiennej pomiędzy kilka kolumn:

Code


#---------------------------------------------------------------------------------------------------
#Podzielone wartości jednej zmiennej pomiędzy kilka kolumn
#Zmienna preferowany rodzaj promocji została podzielona na dwie kolumny

display(Markdown(f"#### Niezgodnośc nr 4. Podzielone wartości jednej zmiennej pomiędzy kilka kolumn: 'preferowany rodzaj promocji','preferowany rodzaj promocji.1"))
display(Markdown(f"* Rozwiązaniem jest dokonanie połączenia (cat) preferowany rodzaj promocji w jedną kolumnę"))

an_02 = ankieta_01a[['id.','preferowany rodzaj promocji','preferowany rodzaj promocji.1']]

ankieta_01['preferowany rodzaj promocji.1']=ankieta_01['preferowany rodzaj promocji.1'].fillna('_')
ankieta_01['rodzaj promocji'] = ankieta_01['preferowany rodzaj promocji'].astype(str).str.cat(ankieta_01['preferowany rodzaj promocji.1'].astype(str), sep=' ')
ankieta_01['rodzaj promocji'] = ankieta_01['rodzaj promocji'].str.replace('_','')    # usunieto zbędne znaki_
ankieta_01= ankieta_01.drop('preferowany rodzaj promocji', axis=1) # usunięcie zbędnej kolumny
ankieta_01= ankieta_01.drop('preferowany rodzaj promocji.1', axis=1) # usunięcie zbędnej kolumny



display_side_by_side(
    an_02.head(), 
    ankieta_01[['id.','rodzaj promocji']].head(), 
    titles=["Oryginalne dane", "Dane po transformacji"]
)

Niezgodnośc nr 4. Podzielone wartości jednej zmiennej pomiędzy kilka kolumn: ‘preferowany rodzaj promocji’,’preferowany rodzaj promocji.1

  • Rozwiązaniem jest dokonanie połączenia (cat) preferowany rodzaj promocji w jedną kolumnę

Oryginalne dane

id. preferowany rodzaj promocji preferowany rodzaj promocji.1
0 R_001 sugestia kasjera
1 R_002 gazetka NaN
2 R_003 reklama RTV
3 R_004 gazetka NaN
4 R_005 gazetka NaN

Dane po transformacji

id. rodzaj promocji
0 R_001 sugestia kasjera
1 R_002 gazetka
2 R_003 reklama RTV
3 R_004 gazetka
4 R_005 gazetka

2.5 Przechowywanie w jednej kolumnie połączonych wartosci wielu zmiennych.

Code


an_04 = ankieta_01[['id.','wiek|liczba osób w rodzinie']]

display(Markdown(f"#### Niezgodnośc nr 5. Przechowywanie w jednej kolumnie połączonych wartosci wielu zmiennych ('wiek|liczba osób w rodzinie') "))
display(Markdown(f"* Rozwiązaniem jest dokonanie podzielenia kolumny (split) ('wiek|liczba osób w rodzinie') na dwie kolumny :(,'wiek', 'liczba osób w rodzinie') "))

ankieta_01[['wiek', 'liczba osób w rodzinie']] = ankieta_01['wiek|liczba osób w rodzinie'].str.split('|', expand=True) # podzielenie kolumny
ankieta_01= ankieta_01.drop('wiek|liczba osób w rodzinie', axis=1) # usunięcie zbędnej kolumny




display_side_by_side(
    an_04.head(), 
    ankieta_01[['id.','wiek', 'liczba osób w rodzinie']].head(), 
    titles=["Oryginalne dane", "Dane po transformacji"]
)

#---------------------------------------------------------------------------------------------------

Niezgodnośc nr 5. Przechowywanie w jednej kolumnie połączonych wartosci wielu zmiennych (‘wiek|liczba osób w rodzinie’)

  • Rozwiązaniem jest dokonanie podzielenia kolumny (split) (‘wiek|liczba osób w rodzinie’) na dwie kolumny :(,‘wiek’, ‘liczba osób w rodzinie’)

Oryginalne dane

id. wiek|liczba osób w rodzinie
0 R_001 30|2
1 R_002 16|1
2 R_003 49|1
3 R_004 67 lat|1
4 R_005 38|5

Dane po transformacji

id. wiek liczba osób w rodzinie
0 R_001 30 2
1 R_002 16 1
2 R_003 49 1
3 R_004 67 lat 1
4 R_005 38 5

2.6 Zmienne podzielonone pomiędzy wiele tabel w wielu plikach

Code



display(Markdown(f"#### Niezgodnośc nr 6. Zmienne podzielonone pomiędzy wiele tabel w wielu plikach: (ankieta_01, ankieta_02, miasta )"))
display(Markdown(f"* Rozwiązaniem jest dokonanie połączenia (merge) 3 tabel w jedną tabelę (baza)) "))

ankieta_01 = pd.merge(ankieta_01, miasta, on = 'kod_miasta',how = 'left')
#ankieta_01= ankieta_01.drop('kod_miasta', axis=1) 
ankieta_02.columns= ankieta_02.columns.to_flat_index()

ankieta_02.columns = ['id.', 'dochody', 'wydatki']

baza= pd.merge(ankieta_01,ankieta_02, on = 'id.')
#baza = baza.drop('kod_miasta')
display(baza.head())



Niezgodnośc nr 6. Zmienne podzielonone pomiędzy wiele tabel w wielu plikach: (ankieta_01, ankieta_02, miasta )

  • Rozwiązaniem jest dokonanie połączenia (merge) 3 tabel w jedną tabelę (baza))
id. kod_miasta PREFEROWANY TYP SKLEPU preferowana marka sklepu preferowanay towar czynnik zakupowy płeć wykształcenie rodzaj promocji wiek liczba osób w rodzinie Miasto Liczba ludności dochody wydatki
0 R_001 M_058 BAZAREK POLOMARKET Napoje 5 cena m podstawowe sugestia kasjera 30 2 Głogów 68170 54 750
1 R_002 M_050 BAZAREK NETTO Owoce i warzywa dostępność m podstawowe gazetka 16 1 Suwałki 69442 26tys 2300
2 R_003 M_069 BAZAREK BIEDRONKA Produkty piekarnicze jakość m podstawowe. reklama RTV 49 1 Świdnica 60281 50 430
3 R_004 M_059 OSIEDLOWY LIDL Produkty piekarnicze jakość m podstawowe gazetka 67 lat 1 Stargard 71464 45 507
4 R_005 M_057 OSIEDLOWY LIDL Mięso i wędliny jakość m zawodowe gazetka 38 5 Ostrowiec Świętokrzyski 69715 28 750

3. Czyszczenie danych

Identyfikacjia nieprawidłowych wartości danych:

Code
import pandas as pd

data_cleaning_summary = pd.DataFrame({
    "Rodzaj nieprawidłowości": [
        "Różna wielkość liter",
        "Niedozwolone znaki alfanumeryczne i znaki specjalne",
        "Błędy literowe, ortograficzne, różna pisownia",
        "Różne miana, jednostki, typy wartości liczbowej",
        "Wartości odstające i nietypowe",
        "Duplikaty",
        "Brakujące dane"
    ],
    "Nazwa kolumn zawierających nieprawidłowe dane": [
        "wszystkie kolumny",
        "wszystkie kolumny",
        "wszystkie kolumny",
        "wiek, liczba osób w rodzinie, dochody, wydatki",
        "wiek, liczba osób w rodzinie, dochody, wydatki",
        "wszystkie kolumny",
        "wszystkie kolumny"
    ],
    "Sposób rozwiązania": [
        "Ujednolicenie wielkości liter za pomocą str.lower().",
        "Usunięcie znaków nie będących literami za pomocą str.replace() oraz użycie regex: [^\w\s], \d, [a-zA-Z]",
        "Poprawa błędów literowych przy użyciu słownika zamian lub funkcji fuzzy matching.",
        "Konwersja wartości do odpowiednich typów danych za pomocą pd.to_numeric() i usunięcie niedozwolonych znaków.",
        "Zdefiniowanie zakresu wartości referencyjnych i obsługa wartości odstających.",
        "Usunięcie zduplikowanych wierszy.",
        "Imputacja brakujących danych."
    ],
    "Użyta funkcja w Python": [
        "str.lower()",
        "str.replace()",
        "własne funkcje",
        "pd.to_numeric() + str.replace()",
        "własne funkcje",
        "df.drop_duplicates()",
        "df.fillna()"
    ]
})

# Display the updated summary table
display(data_cleaning_summary)
Rodzaj nieprawidłowości Nazwa kolumn zawierających nieprawidłowe dane Sposób rozwiązania Użyta funkcja w Python
0 Różna wielkość liter wszystkie kolumny Ujednolicenie wielkości liter za pomocą str.lower(). str.lower()
1 Niedozwolone znaki alfanumeryczne i znaki specjalne wszystkie kolumny Usunięcie znaków nie będących literami za pomocą str.replace() oraz użycie regex: [^\w\s], \d, [a-zA-Z] str.replace()
2 Błędy literowe, ortograficzne, różna pisownia wszystkie kolumny Poprawa błędów literowych przy użyciu słownika zamian lub funkcji fuzzy matching. własne funkcje
3 Różne miana, jednostki, typy wartości liczbowej wiek, liczba osób w rodzinie, dochody, wydatki Konwersja wartości do odpowiednich typów danych za pomocą pd.to_numeric() i usunięcie niedozwolonych znaków. pd.to_numeric() + str.replace()
4 Wartości odstające i nietypowe wiek, liczba osób w rodzinie, dochody, wydatki Zdefiniowanie zakresu wartości referencyjnych i obsługa wartości odstających. własne funkcje
5 Duplikaty wszystkie kolumny Usunięcie zduplikowanych wierszy. df.drop_duplicates()
6 Brakujące dane wszystkie kolumny Imputacja brakujących danych. df.fillna()

3.1. Różna wielkość liter

Code

display(Markdown(f"* Rozwiązaniem jest - Ujednolicenie wielkości"))

text_małe_litery= ['PREFEROWANY TYP SKLEPU','preferowanay towar',   'czynnik zakupowy', 'płeć'  ,'wykształcenie',   'rodzaj promocji']

display(Markdown(f"* Lista kolumn, które muszą być z małej litery:    {text_małe_litery}"))

text_duze_litery= ['preferowana marka sklepu']
display(Markdown(f"* Lista kolumn, które muszą być z małej litey:     {text_duze_litery}"))



display(Markdown(f"* tabela przed zmianami:"))
display(baza.head(3))

    # Apply changes
for col in text_małe_litery:
    baza[col] = baza[col].str.lower()

for col in text_duze_litery:
    baza[col] = baza[col].str.upper()

display(Markdown(f"* tabela po zmianach:"))
display(baza.head(3))
  • Rozwiązaniem jest - Ujednolicenie wielkości
  • Lista kolumn, które muszą być z małej litery: [‘PREFEROWANY TYP SKLEPU’, ‘preferowanay towar’, ‘czynnik zakupowy’, ‘płeć’, ‘wykształcenie’, ‘rodzaj promocji’]
  • Lista kolumn, które muszą być z małej litey: [‘preferowana marka sklepu’]
  • tabela przed zmianami:
id. kod_miasta PREFEROWANY TYP SKLEPU preferowana marka sklepu preferowanay towar czynnik zakupowy płeć wykształcenie rodzaj promocji wiek liczba osób w rodzinie Miasto Liczba ludności dochody wydatki
0 R_001 M_058 BAZAREK POLOMARKET Napoje 5 cena m podstawowe sugestia kasjera 30 2 Głogów 68170 54 750
1 R_002 M_050 BAZAREK NETTO Owoce i warzywa dostępność m podstawowe gazetka 16 1 Suwałki 69442 26tys 2300
2 R_003 M_069 BAZAREK BIEDRONKA Produkty piekarnicze jakość m podstawowe. reklama RTV 49 1 Świdnica 60281 50 430
  • tabela po zmianach:
id. kod_miasta PREFEROWANY TYP SKLEPU preferowana marka sklepu preferowanay towar czynnik zakupowy płeć wykształcenie rodzaj promocji wiek liczba osób w rodzinie Miasto Liczba ludności dochody wydatki
0 R_001 M_058 bazarek POLOMARKET napoje 5 cena m podstawowe sugestia kasjera 30 2 Głogów 68170 54 750
1 R_002 M_050 bazarek NETTO owoce i warzywa dostępność m podstawowe gazetka 16 1 Suwałki 69442 26tys 2300
2 R_003 M_069 bazarek BIEDRONKA produkty piekarnicze jakość m podstawowe. reklama rtv 49 1 Świdnica 60281 50 430

3.2. Niedozwolone znaki alfanumeryczne i specjalne

Code


display(Markdown(f"* Rozwiązaniem jest :"))
display(Markdown(f"- dla wszystkich kolumn:  Usunięcie zbędnych znaków specjalnych"))
display(Markdown(f"- dla kolumn liczbowych:  Usunięcie zbędnych znaków alfabetycznych"))
display(Markdown(f"- dla kolumn tekstowych:  Usunięcie zbędnych znaków numerycznych"))


kolumny_textowe= ['PREFEROWANY TYP SKLEPU','preferowanay towar',    'czynnik zakupowy', 'płeć'  ,'wykształcenie',   'rodzaj promocji']

display(Markdown(f"* Lista kolumn tekstowych do sprawdzenia  {kolumny_textowe}"))

kolumny_liczbowe = ['wiek','liczba osób w rodzinie',    'Liczba ludności',  'dochody'   ,'wydatki']
display(Markdown(f"* Lista kolumn liczbowych do sprawdzenia  {kolumny_liczbowe}"))


print()
display(Markdown(f"### Tabela błędnych wpisów:"))




import pandas as pd
import re

def sprawdz_bledy(baza):
    """
    Funkcja sprawdza błędy w DataFrame 'baza'.
    
    Sprawdzane są:
      1. Wartości zawierające znaki specjalne w kolumnach tekstowych (wszystkie kolumny typu object).
      2. Wartości tekstowe zawierające cyfry w wybranych kolumnach (kolumny_textowe).
      3. Wartości liczbowe (przyjmowane jako tekst) zawierające litery w wybranych kolumnach (kolumny_liczbowe).
      
    Zwracany DataFrame zawiera kolumny:
      - "rodzaj błędu"
      - "kolumna"
      - "id."
      - "nr indexu"
      - "błędne dane" (lista wykrytych wartości)
    """
    
    # Definicje kolumn (można modyfikować według potrzeb)
    kolumny_textowe = ['PREFEROWANY TYP SKLEPU', 'preferowanay towar', 'czynnik zakupowy', 'płeć', 'wykształcenie', 'rodzaj promocji']
    kolumny_liczbowe = ['wiek', 'liczba osób w rodzinie', 'Liczba ludności', 'dochody', 'wydatki']
    
    # Lista, do której będziemy dodawać informacje o błędach
    lista_bledow = []
    
    # 1. Sprawdzenie kolumn typu object pod kątem znaków specjalnych
    for col in baza.columns:
        if baza[col].dtype == 'object':
            maska_specjalne = baza[col].str.contains(r'[^\w\s]', na=False)
            if maska_specjalne.any():
                for idx in baza[maska_specjalne].index:
                    wartosc = baza.loc[idx, col]
                    id_val = baza.loc[idx, 'id.'] if 'id.' in baza.columns else None
                    lista_bledow.append({
                        "rodzaj błędu": "znaki specjalne",
                        "kolumna": col,
                        "id.": id_val,
                        "nr indexu": idx,
                        "błędne dane": [wartosc]
                    })
                    
    # 2. Sprawdzenie wybranych kolumn tekstowych, czy zawierają cyfry
    for col in kolumny_textowe:
        if col in baza.columns and baza[col].dtype == 'object':
            maska_cyfry = baza[col].str.contains(r'\d', na=False)
            if maska_cyfry.any():
                for idx in baza[maska_cyfry].index:
                    wartosc = baza.loc[idx, col]
                    id_val = baza.loc[idx, 'id.'] if 'id.' in baza.columns else None
                    lista_bledow.append({
                        "rodzaj błędu": "tekst zawiera cyfry",
                        "kolumna": col,
                        "id.": id_val,
                        "nr indexu": idx,
                        "błędne dane": [wartosc]
                    })
                    
    # 3. Sprawdzenie wybranych kolumn liczbowych (reprezentowanych jako tekst) pod kątem liter
    for col in kolumny_liczbowe:
        if col in baza.columns and baza[col].dtype == 'object':
            maska_litery = baza[col].str.contains(r'[a-zA-Z]', na=False)
            if maska_litery.any():
                for idx in baza[maska_litery].index:
                    wartosc = baza.loc[idx, col]
                    id_val = baza.loc[idx, 'id.'] if 'id.' in baza.columns else None
                    lista_bledow.append({
                        "rodzaj błędu": "liczba zawiera litery",
                        "kolumna": col,
                        "id.": id_val,
                        "nr indexu": idx,
                        "błędne dane": [wartosc]
                    })
    
    # Utworzenie DataFrame z wykrytymi błędami
    df_bledy = pd.DataFrame(lista_bledow, columns=["rodzaj błędu", "kolumna", "id.", "nr indexu", "błędne dane"])
    
    # Agregacja – grupujemy błędy wg "rodzaj błędu" oraz "kolumna"
    if not df_bledy.empty:
        df_bledy = df_bledy.groupby(["rodzaj błędu", "kolumna"]).agg({
            "id.": lambda x: list(x),
            "nr indexu": lambda x: list(x),
            "błędne dane": lambda x: sum(x, [])
        }).reset_index()
    
    return df_bledy






display(sprawdz_bledy(baza))





import pandas as pd
import re
from collections import Counter

def napraw_znaki(baza):
    """
    Funkcja dokonuje czyszczenia niedozwolonych znaków w DataFrame 'baza',
    modyfikując zawartość komórek poprzez usunięcie jedynie niedozwolonych znaków,
    bez usuwania wierszy.
    
    Operacje:
      1. Dla wszystkich kolumn typu object usuwamy znaki niealfanumeryczne 
         (czyli wszystko poza literami, cyframi, podkreśleniem i spacjami).
      2. W kolumnach tekstowych (lista kolumn: kolumny_textowe) usuwamy cyfry.
      3. W kolumnach liczbowych (lista kolumn: kolumny_liczbowe) usuwamy litery 
         (zarówno angielskie, jak i polskie znaki).
      4. W kolumnach liczbowych usuwamy polskie znaki diakrytyczne.
      
    Dla każdej zmiany generowany jest raport zawierający:
       - "rodzaj operacji"
       - "kolumna"
       - "nr indexu"
       - "stara wartość"
       - "nowa wartość"
       - "usunięte znaki" – znaki, które zostały usunięte
       
    Zwracana jest krotka: (baza_clean, raport_df)
    """
    # Funkcja pomocnicza do wyznaczania usuniętych znaków
    def get_removed_characters(old, new):
        counter_old = Counter(old)
        counter_new = Counter(new)
        removed_chars = []
        for ch in counter_old:
            diff = counter_old[ch] - counter_new.get(ch, 0)
            if diff > 0:
                removed_chars.append(ch * diff)
        return ''.join(removed_chars)
    
    baza_clean = baza.copy()
    raport_list = []
    
    # Operacja 1: Usunięcie znaków niealfanumerycznych ze wszystkich kolumn typu object.
    for col in baza_clean.columns:
        if baza_clean[col].dtype == 'object':
            for idx, val in baza_clean[col].items():
                if isinstance(val, str):
                    new_val = re.sub(r'[^\w\s]', '', val)
                    if new_val != val:
                        removed = get_removed_characters(val, new_val)
                        raport_list.append({
                            "rodzaj operacji": "usunięcie znaków niealfanumerycznych",
                            "kolumna": col,
                            "nr indexu": idx,
                            "stara wartość": val,
                            "nowa wartość": new_val,
                            "usunięte znaki": removed
                        })
                        baza_clean.at[idx, col] = new_val
    
    # Definicje list kolumn
    kolumny_textowe = ['PREFEROWANY TYP SKLEPU', 'preferowanay towar', 'czynnik zakupowy', 'płeć', 'wykształcenie', 'rodzaj promocji']
    kolumny_liczbowe = ['wiek', 'liczba osób w rodzinie', 'Liczba ludności', 'dochody', 'wydatki']
    
    # Operacja 2: W kolumnach tekstowych usuwamy cyfry.
    for col in kolumny_textowe:
        if col in baza_clean.columns and baza_clean[col].dtype == 'object':
            for idx, val in baza_clean[col].items():
                if isinstance(val, str):
                    new_val = re.sub(r'\d', '', val)
                    if new_val != val:
                        removed = get_removed_characters(val, new_val)
                        raport_list.append({
                            "rodzaj operacji": "usunięcie cyfr z tekstu",
                            "kolumna": col,
                            "nr indexu": idx,
                            "stara wartość": val,
                            "nowa wartość": new_val,
                            "usunięte znaki": removed
                        })
                        baza_clean.at[idx, col] = new_val
                        
    # Operacja 3: W kolumnach liczbowych usuwamy litery (angielskie oraz polskie).
    pattern_litery = r'[a-zA-ZąćęłńóśźżĄĆĘŁŃÓŚŹŻ]'
    for col in kolumny_liczbowe:
        if col in baza_clean.columns and baza_clean[col].dtype == 'object':
            for idx, val in baza_clean[col].items():
                if isinstance(val, str):
                    new_val = re.sub(pattern_litery, '', val)
                    if new_val != val:
                        removed = get_removed_characters(val, new_val)
                        raport_list.append({
                            "rodzaj operacji": "usunięcie liter z kolumny liczbowej",
                            "kolumna": col,
                            "nr indexu": idx,
                            "stara wartość": val,
                            "nowa wartość": new_val,
                            "usunięte znaki": removed
                        })
                        baza_clean.at[idx, col] = new_val
                        
    # Operacja 4: W kolumnach liczbowych usuwamy polskie znaki diakrytyczne.
    polish_diacritics = {ord(ch): None for ch in "ąćęłńóśźżĄĆĘŁŃÓŚŹŻ"}
    for col in kolumny_liczbowe:
        if col in baza_clean.columns and baza_clean[col].dtype == 'object':
            for idx, val in baza_clean[col].items():
                if isinstance(val, str):
                    new_val = val.translate(polish_diacritics)
                    if new_val != val:
                        removed = get_removed_characters(val, new_val)
                        raport_list.append({
                            "rodzaj operacji": "usunięcie polskich znaków diakrytycznych",
                            "kolumna": col,
                            "nr indexu": idx,
                            "stara wartość": val,
                            "nowa wartość": new_val,
                            "usunięte znaki": removed
                        })
                        baza_clean.at[idx, col] = new_val
                        
    raport_df = pd.DataFrame(raport_list, 
                             columns=["rodzaj operacji", "kolumna", "nr indexu", "stara wartość", "nowa wartość", "usunięte znaki"])
    
    return baza_clean, raport_df











from IPython.display import display, Markdown

# Wywołanie funkcji napraw_znaki
baza_naprawiona, raport_naprawy = napraw_znaki(baza)



# Wyświetlenie raportu z wykonanych operacji
display(Markdown("### Raport Naprawy"))
display(raport_naprawy)




baza = baza_naprawiona

display(sprawdz_bledy(baza))
  • Rozwiązaniem jest :
  • dla wszystkich kolumn: Usunięcie zbędnych znaków specjalnych
  • dla kolumn liczbowych: Usunięcie zbędnych znaków alfabetycznych
  • dla kolumn tekstowych: Usunięcie zbędnych znaków numerycznych
  • Lista kolumn tekstowych do sprawdzenia [‘PREFEROWANY TYP SKLEPU’, ‘preferowanay towar’, ‘czynnik zakupowy’, ‘płeć’, ‘wykształcenie’, ‘rodzaj promocji’]
  • Lista kolumn liczbowych do sprawdzenia [‘wiek’, ‘liczba osób w rodzinie’, ‘Liczba ludności’, ‘dochody’, ‘wydatki’]

Tabela błędnych wpisów:

rodzaj błędu kolumna id. nr indexu błędne dane
0 liczba zawiera litery dochody [R_002, R_007] [1, 6] [26tys, 29 zł]
1 liczba zawiera litery wiek [R_004, R_012] [3, 11] [67 lat, 33 lata]
2 liczba zawiera litery wydatki [R_021] [20] [669 PLN]
3 tekst zawiera cyfry preferowanay towar [R_001] [0] [napoje 5]
4 tekst zawiera cyfry rodzaj promocji [R_014] [13] [2gazetka ]
5 tekst zawiera cyfry wykształcenie [R_053] [52] [śred5nie]
6 znaki specjalne dochody [R_011] [10] [-31]
7 znaki specjalne wydatki [R_006] [5] [-556]
8 znaki specjalne wykształcenie [R_003, R_068, R_095] [2, 76, 103] [podstawowe., średnie?, wyższe,]

Raport Naprawy

rodzaj operacji kolumna nr indexu stara wartość nowa wartość usunięte znaki
0 usunięcie znaków niealfanumerycznych wykształcenie 2 podstawowe. podstawowe .
1 usunięcie znaków niealfanumerycznych wykształcenie 76 średnie? średnie ?
2 usunięcie znaków niealfanumerycznych wykształcenie 103 wyższe, wyższe ,
3 usunięcie znaków niealfanumerycznych dochody 10 -31 31 -
4 usunięcie znaków niealfanumerycznych wydatki 5 -556 556 -
5 usunięcie cyfr z tekstu preferowanay towar 0 napoje 5 napoje 5
6 usunięcie cyfr z tekstu wykształcenie 52 śred5nie średnie 5
7 usunięcie cyfr z tekstu rodzaj promocji 13 2gazetka gazetka 2
8 usunięcie liter z kolumny liczbowej wiek 3 67 lat 67 lat
9 usunięcie liter z kolumny liczbowej wiek 11 33 lata 33 laat
10 usunięcie liter z kolumny liczbowej dochody 1 26tys 26 tys
11 usunięcie liter z kolumny liczbowej dochody 6 29 zł 29
12 usunięcie liter z kolumny liczbowej wydatki 20 669 PLN 669 PLN
rodzaj błędu kolumna id. nr indexu błędne dane

3.3. Błędy literowe, ortograficzne, różna pisowania

Code


display(Markdown(f"Rozwiązaniem jest: "))
display(Markdown(f"* Sprawdzenie, w których kolumnach występują niezgodności "))
display(Markdown(f"* Dokonanie zmian błednych danych, "))
print()
display(Markdown(f"Lista unikatowych wartości: "))
kolumny_textowe= ['PREFEROWANY TYP SKLEPU','preferowanay towar',    'czynnik zakupowy', 'płeć'  ,'wykształcenie',   'rodzaj promocji']
for col in kolumny_textowe:
    unique_values = baza[col].unique()
    display(Markdown(f"* Unikatowe wartości w '{col}' : {unique_values}"))


kolumny_do_zmiany = ['PREFEROWANY TYP SKLEPU', 'wykształcenie' ]
display(Markdown(f"Po analize ustalono, że błedy występują w kolumnach: {kolumny_do_zmiany}: "))
kol_wyksz_błędy = ['średn' , 'srednie' 'wyzsze']
kol_typ_sklep = ['bazar' ]

# Tworzenie maski dla błędnych wartości w kolumnach do zmiany
mask_wykształcenie = ~baza['wykształcenie'].isin(['podstawowe', 'zawodowe', 'średnie', 'wyższe'])
mask_typ_sklepu = ~baza['PREFEROWANY TYP SKLEPU'].isin(['bazarek', 'osiedlowy', 'supermarket', 'galeria'])

# Wyświetlanie DataFrame z błędnymi wartościami
błędne_wykształcenie = baza[mask_wykształcenie]
błędny_typ_sklepu = baza[mask_typ_sklepu]


baza['wykształcenie'] = baza['wykształcenie'].replace({
    'średn': 'średnie',
    'srednie': 'średnie',
    'wyzsze': 'wyższe'
})

# Poprawa błędnych wartości w kolumnie 'PREFEROWANY TYP SKLEPU'
baza['PREFEROWANY TYP SKLEPU'] = baza['PREFEROWANY TYP SKLEPU'].replace({
    'bazar': 'bazarek',
    'super': 'supermarket'
})

# Wyświetlanie DataFrame z poprawionymi wartościami
błędne_wykształcenie_poprawione = baza.loc[błędne_wykształcenie.index, ['id.', 'wykształcenie']]
błędny_typ_sklepu_poprawione = baza.loc[błędny_typ_sklepu.index, ['id.', 'PREFEROWANY TYP SKLEPU']]



display_side_by_side(
    błędne_wykształcenie[['id.', 'wykształcenie']], 
    błędne_wykształcenie_poprawione[['id.', 'wykształcenie']], 
    titles=["Błędne wartości w kolumnie 'wykształcenie'", "Poprawione wartości w kolumnie 'wykształcenie'"]
)

display_side_by_side(
    błędny_typ_sklepu[['id.', 'PREFEROWANY TYP SKLEPU']], 
    błędny_typ_sklepu_poprawione[['id.', 'PREFEROWANY TYP SKLEPU']], 
    titles=["Błędne wartości w kolumnie 'PREFEROWANY TYP SKLEPU'", "Poprawione wartości w kolumnie 'PREFEROWANY TYP SKLEPU'"]
)

Rozwiązaniem jest:

  • Sprawdzenie, w których kolumnach występują niezgodności
  • Dokonanie zmian błednych danych,

Lista unikatowych wartości:

  • Unikatowe wartości w ‘PREFEROWANY TYP SKLEPU’ : [‘bazarek’ ‘osiedlowy’ ‘supermarket’ ‘galeria’ ‘super’ ‘bazar’ nan]
  • Unikatowe wartości w ‘preferowanay towar’ : [‘napoje’ ‘owoce i warzywa’ ‘produkty piekarnicze’ ‘mięso i wędliny’ ‘produkty zbożowe’ ‘napoje’ ‘słodycze’ ‘produkty mleczne’]
  • Unikatowe wartości w ‘czynnik zakupowy’ : [‘cena’ ‘dostępność’ ‘jakość’ ‘marka’ ‘skład’ ‘lokalność’ ‘opinie’ ‘opakowanie’ ‘preferencje kulinarne’]
  • Unikatowe wartości w ‘płeć’ : [‘m’ ‘k’]
  • Unikatowe wartości w ‘wykształcenie’ : [‘podstawowe’ ‘zawodowe’ ‘średn’ ‘średnie’ ‘wyższe’ ‘srednie’ ‘wyzsze’]
  • Unikatowe wartości w ‘rodzaj promocji’ : [‘sugestia kasjera’ ‘gazetka’ ‘reklama rtv’ ‘karta’ ‘sms’ ‘email’ ‘aplikacja’ ‘nie korzystam’ ‘nan’]

Po analize ustalono, że błedy występują w kolumnach: [‘PREFEROWANY TYP SKLEPU’, ‘wykształcenie’]:

Błędne wartości w kolumnie 'wykształcenie'

id. wykształcenie
14 R_015 średn
69 R_061 srednie
92 R_084 wyzsze

Poprawione wartości w kolumnie 'wykształcenie'

id. wykształcenie
14 R_015 średnie
69 R_061 średnie
92 R_084 wyższe

Błędne wartości w kolumnie 'PREFEROWANY TYP SKLEPU'

id. PREFEROWANY TYP SKLEPU
71 R_063 super
79 R_071 super
84 R_076 bazar
85 R_077 bazar
120 R_112 NaN
126 R_118 NaN

Poprawione wartości w kolumnie 'PREFEROWANY TYP SKLEPU'

id. PREFEROWANY TYP SKLEPU
71 R_063 supermarket
79 R_071 supermarket
84 R_076 bazarek
85 R_077 bazarek
120 R_112 NaN
126 R_118 NaN

3.4. Różne miana/jednostki oraz typ wartosci liczbowej

Code


kolumny_liczbowe = ['wiek','liczba osób w rodzinie',    'Liczba ludności',  'dochody'   ,'wydatki']


for col in kolumny_liczbowe:
    print(f"Kolumna: {col}, Typ zmiennej: {baza[col].dtype}")

   
baza['wiek'] = baza['wiek'].astype(int)
baza['liczba osób w rodzinie'] = baza['liczba osób w rodzinie'].astype(int)
baza['dochody'] = baza['dochody'].astype('int64')
baza['wydatki'] = baza['wydatki'].astype('int64')





print("po zmianie:)")
for col in kolumny_liczbowe:
    print(f"Kolumna: {col}, Typ zmiennej: {baza[col].dtype}")


#dochody są w tyś : nalezy zamieniev na cześci pojedyńcze tj przemnozyć  *1000
#wydatki są wyrażone w skali miesiąca należy zmień skalę na rok *12



baza['dochody']=baza['dochody']*1000
baza['wydatki']=baza['wydatki']*12

Kolumna: wiek, Typ zmiennej: object
Kolumna: liczba osób w rodzinie, Typ zmiennej: object
Kolumna: Liczba ludności, Typ zmiennej: int64
Kolumna: dochody, Typ zmiennej: object
Kolumna: wydatki, Typ zmiennej: object
po zmianie:)
Kolumna: wiek, Typ zmiennej: int64
Kolumna: liczba osób w rodzinie, Typ zmiennej: int64
Kolumna: Liczba ludności, Typ zmiennej: int64
Kolumna: dochody, Typ zmiennej: int64
Kolumna: wydatki, Typ zmiennej: int64

3.5. Wartości odstające i nietypowe

Code

#--------------------------------------------------------------------------------------------------------------------------------------

display(Markdown(f"#### Niezgodnośc nr 7.  kolumny zmiennych liczbowych zawierają wartosci nietypowe lub odstające "))
display(Markdown(f"* Rozwiązaniem jest - zdefiniowanie zakresu wartosci referencyjnych dla zmiennych, "))
display(Markdown(f"* Sprawdzenie, w których kolumnach występują niezgodności "))
display(Markdown(f"* Dokonanie zmian błednych danych lub zamianę na NaN "))

display(Markdown(f"* Wartości referencyjne dla zmiennych liczbowych :"))

dane = {
    "Cecha": [
        "wiek",
        "liczba osób w rodzinie",
        "dochody",
        "wydatki",
        "dodatkowy parametr"
       
    ],
    "Dozwolone wartości": [
        "18 - 105",
        "1 - 10",
        "> 0",
        "> 0",
        "dochody > wydatki"
       
    ]
}

df = pd.DataFrame(dane)

display(df)

print()
display(Markdown(f"###Sprawdzenie czy w zmiennych liczbowych znajduję się wartości z poza zakresu referencyjnego oraz wartosci odstające na podstawie miar statystycznych"))


print()
 
import altair as alt
import pandas as pd

# Załóżmy, że baza to DataFrame
# baza = pd.read_csv('twoj_plik.csv')

# Boxploty
box_dochody = alt.Chart(baza).mark_boxplot().encode(
    x=alt.X('dochody', title='Dochody')
).properties(title='Boxplot for Dochody')

box_wydatki = alt.Chart(baza).mark_boxplot().encode(
    x=alt.X('wydatki', title='Wydatki')
).properties(title='Boxplot for Wydatki')

box_wiek = alt.Chart(baza).mark_boxplot().encode(
    x=alt.X('wiek', title='Wiek')
).properties(title='Boxplot for Wiek')

box_liczba_osob = alt.Chart(baza).mark_boxplot().encode(
    x=alt.X('liczba osób w rodzinie', title='Liczba Osób w Rodzinie')
).properties(title='Boxplot for Liczba Osób w Rodzinie')

# Łączenie boxplotów w jednej linii
boxplots = alt.hconcat(box_dochody, box_wydatki, box_wiek, box_liczba_osob)
boxplots.display()

# Histogramy
hist_dochody = alt.Chart(baza).mark_bar(opacity=0.7).encode(
    x=alt.X('dochody', bin=alt.Bin(maxbins=30), title='Dochody'),
    y=alt.Y('count()', title='Frequency')
).properties(title='Histogram for Dochody')

hist_wydatki = alt.Chart(baza).mark_bar(opacity=0.7).encode(
    x=alt.X('wydatki', bin=alt.Bin(maxbins=30), title='Wydatki'),
    y=alt.Y('count()', title='Frequency')
).properties(title='Histogram for Wydatki')

hist_wiek = alt.Chart(baza).mark_bar(opacity=0.7).encode(
    x=alt.X('wiek', bin=alt.Bin(maxbins=30), title='Wiek'),
    y=alt.Y('count()', title='Frequency')
).properties(title='Histogram for Wiek')

hist_liczba_osob = alt.Chart(baza).mark_bar(opacity=0.7).encode(
    x=alt.X('liczba osób w rodzinie', bin=alt.Bin(maxbins=30), title='Liczba Osób w Rodzinie'),
    y=alt.Y('count()', title='Frequency')
).properties(title='Histogram for Liczba Osób w Rodzinie')

# Łączenie histogramów w jednej linii
histograms = alt.hconcat(hist_dochody, hist_wydatki, hist_wiek, hist_liczba_osob)
histograms.display()


display(Markdown(f"Tabela wartości nietypowych i odstających:"))
print()







import pandas as pd
import numpy as np
from scipy import stats

def sprawdz_wartosci_referencyjne(baza):
    """
    Funkcja sprawdza zgodność zmiennych z wartościami referencyjnymi.
    Zwraca oczyszczony DataFrame oraz raport z niezgodnościami.
    """
    baza_clean = baza.copy()
    raport_zakres = []

    # Warunki zgodnie z wartościami referencyjnymi:
    warunki = {
        'dochody': (baza_clean['dochody'] <= 0),
        'wydatki': (baza_clean['wydatki'] < 0),
        'wiek': (baza_clean['wiek'] < 15) | (baza_clean['wiek'] > 105),
        'liczba osób w rodzinie': (baza_clean['liczba osób w rodzinie'] < 1) | (baza_clean['liczba osób w rodzinie'] > 10)
    }

    # Sprawdzenie niezgodności względem zakresu referencyjnego:
    for zmienna, warunek in warunki.items():
        if warunek.any():
            indeksy = baza_clean.index[warunek].tolist()
            ids = baza_clean.loc[warunek, 'id.'].tolist() if 'id.' in baza_clean.columns else None
            wartosci = baza_clean.loc[warunek, zmienna].tolist()
            raport_zakres.append({
                "zmienna": zmienna,
                "sposób identyfikacji": "zakres referencyjny",
                "indeksy": indeksy,
                "id.": ids,
                "wartości niezgodne": wartosci,
                "ilość": len(indeksy),
                "nowa wartość": [np.nan]*len(indeksy)
            })
            baza_clean.loc[warunek, zmienna] = np.nan

    # Sprawdzenie niezgodności między 'wydatki' a 'dochody': wydatki > dochody
    warunek_wydatki_dochody = baza_clean['wydatki'] > baza_clean['dochody']
    if warunek_wydatki_dochody.any():
        indeksy = baza_clean.index[warunek_wydatki_dochody].tolist()
        ids = baza_clean.loc[warunek_wydatki_dochody, 'id.'].tolist() if 'id.' in baza_clean.columns else None
        dochody_wart = baza_clean.loc[warunek_wydatki_dochody, 'dochody'].tolist()
        wydatki_wart = baza_clean.loc[warunek_wydatki_dochody, 'wydatki'].tolist()
        raport_zakres.append({
            "zmienna": "dochody, wydatki",
            "sposób identyfikacji": "wydatki > dochody",
            "indeksy": indeksy,
            "id.": ids,
            "wartości niezgodne": {"dochody": dochody_wart, "wydatki": wydatki_wart},
            "ilość": len(indeksy),
            "nowa wartość": [np.nan]*len(indeksy)
        })
        baza_clean.loc[warunek_wydatki_dochody, ['dochody', 'wydatki']] = np.nan

    raport_zakres_df = pd.DataFrame(raport_zakres, columns=["zmienna", "sposób identyfikacji", "indeksy", "id.", "wartości niezgodne", "ilość", "nowa wartość"])
    return baza_clean, raport_zakres_df




def sprawdz_wartosci_odstajace(baza):
    """
    Funkcja identyfikuje wartości odstające za pomocą metod IQR i Z-score.
    Zwraca oczyszczony DataFrame oraz raport z niezgodnościami.
    """
    baza_clean = baza.copy()
    raport_stat = []

    # Funkcja pomocnicza – identyfikacja wartości odstających wg metody IQR
    def identify_outliers_iqr(series):
        Q1 = series.quantile(0.25)
        Q3 = series.quantile(0.75)
        IQR = Q3 - Q1
        lower_bound = Q1 - 1.5 * IQR
        upper_bound = Q3 + 1.5 * IQR
        mask = (series < lower_bound) | (series > upper_bound)
        return mask

    # Funkcja pomocnicza – identyfikacja wartości odstających wg Z-score
    def identify_outliers_zscore(series):
        z_scores = stats.zscore(series.dropna())
        mask = pd.Series(np.abs(z_scores) > 3, index=series.dropna().index)
        return mask

    numeric_vars = ['dochody', 'wydatki', 'wiek', 'liczba osób w rodzinie']

    for zmienna in numeric_vars:
        if zmienna in baza_clean.columns:
            # IQR
            mask_iqr = identify_outliers_iqr(baza_clean[zmienna])
            if mask_iqr.any():
                indeksy = baza_clean.index[mask_iqr].tolist()
                ids = baza_clean.loc[mask_iqr, 'id.'].tolist() if 'id.' in baza_clean.columns else None
                wartosci = baza_clean.loc[mask_iqr, zmienna].tolist()
                raport_stat.append({
                    "zmienna": zmienna,
                    "sposób identyfikacji": "IQR",
                    "indeksy": indeksy,
                    "id.": ids,
                    "wartości niezgodne": wartosci,
                    "ilość": len(indeksy),
                    "nowa wartość": [np.nan]*len(indeksy)
                })
                baza_clean.loc[mask_iqr, zmienna] = np.nan

            # Z-score
            mask_zscore = identify_outliers_zscore(baza_clean[zmienna])
            if mask_zscore.any():
                indeksy = mask_zscore.index.tolist()
                ids = baza_clean.loc[indeksy, 'id.'].tolist() if 'id.' in baza_clean.columns else None
                wartosci = baza_clean.loc[indeksy, zmienna].tolist()
                raport_stat.append({
                    "zmienna": zmienna,
                    "sposób identyfikacji": "Z-score",
                    "indeksy": indeksy,
                    "id.": ids,
                    "wartości niezgodne": wartosci,
                    "ilość": len(indeksy),
                    "nowa wartość": [np.nan]*len(indeksy)
                })
                baza_clean.loc[indeksy, zmienna] = np.nan

    raport_stat_df = pd.DataFrame(raport_stat, columns=["zmienna", "sposób identyfikacji", "indeksy", "id.", "wartości niezgodne", "ilość", "nowa wartość"])
    return baza_clean, raport_stat_df



# Sprawdzenie wartości referencyjnych
baza_clean, raport_zakres_df = sprawdz_wartosci_referencyjne(baza)
display( raport_zakres_df)

# Sprawdzenie wartości odstających
baza_clean_final, raport_stat_df = sprawdz_wartosci_odstajace(baza_clean)
display(raport_stat_df)


baza = baza_clean_final

Niezgodnośc nr 7. kolumny zmiennych liczbowych zawierają wartosci nietypowe lub odstające

  • Rozwiązaniem jest - zdefiniowanie zakresu wartosci referencyjnych dla zmiennych,
  • Sprawdzenie, w których kolumnach występują niezgodności
  • Dokonanie zmian błednych danych lub zamianę na NaN
  • Wartości referencyjne dla zmiennych liczbowych :
Cecha Dozwolone wartości
0 wiek 18 - 105
1 liczba osób w rodzinie 1 - 10
2 dochody > 0
3 wydatki > 0
4 dodatkowy parametr dochody > wydatki

###Sprawdzenie czy w zmiennych liczbowych znajduję się wartości z poza zakresu referencyjnego oraz wartosci odstające na podstawie miar statystycznych

Tabela wartości nietypowych i odstających:

zmienna sposób identyfikacji indeksy id. wartości niezgodne ilość nowa wartość
0 dochody zakres referencyjny [5, 86, 112] [R_006, R_078, R_104] [0, 0, 0] 3 [nan, nan, nan]
1 wiek zakres referencyjny [6] [R_007] [158] 1 [nan]
2 liczba osób w rodzinie zakres referencyjny [39] [R_040] [30] 1 [nan]
3 dochody, wydatki wydatki > dochody [1] [R_002] {'dochody': [26000.0], 'wydatki': [27600]} 1 [nan]
zmienna sposób identyfikacji indeksy id. wartości niezgodne ilość nowa wartość
0 dochody IQR [18] [R_019] [99000.0] 1 [nan]
1 wydatki IQR [2, 13, 14, 125, 126, 129] [R_003, R_014, R_015, R_117, R_118, R_121] [5160.0, 4200.0, 4200.0, 14964.0, 14964.0, 14964.0] 6 [nan, nan, nan, nan, nan, nan]

3.6. Duplikaty

Code

#---------------------------------------------------------------------------------------------------

display(Markdown(f"Sprawdzenie czy w danych występują duplikaty"))

display(Markdown(f"Identyfikacja:"))

display(Markdown(f"- Czy w tabeli danych znajdują się zduplikowane-powielone * KOLUMNY * (z tymi samymi wartosciami)? :"))


zduplikowane_kolumny = baza.columns[baza.columns.duplicated()].tolist()

if zduplikowane_kolumny:
    display(Markdown(f"* Zduplikowane kolumny: {zduplikowane_kolumny}"))
else:
    display(Markdown(f"     -Brak zduplikowanych kolumn"))




display(Markdown(f"- Czy w tabeli danych znajdują się zduplikowane-powielone  * WIERSZE * (z tymi samymi wartosciami)? :"))


if baza.duplicated().any():
    display(Markdown(f"     -Zduplikowane wiersze: {baza[baza.duplicated()].shape[0]}"))
    display(Markdown(f"Rozmiar tabeli z duplikatami: {baza.shape}"))
    display(baza[baza.duplicated()])
else:
    display(Markdown(f"     -Brak zduplikowanych wierszy"))
    


display(Markdown(f"* Rozwiązaniem jest dokonanie zidentyfokowanie dublujących się danych , a następnie ich usunięcie "))
print(f'UWAGA: Usunięto wiersze ({sum(ankieta_01a.duplicated())}) z duplikującymi się danymi')
baza.drop_duplicates(inplace=True)


display(Markdown(f"Rozmiar tabeli po usunięciu duplikatów: {baza.shape}"))

print()

Sprawdzenie czy w danych występują duplikaty

Identyfikacja:

  • Czy w tabeli danych znajdują się zduplikowane-powielone * KOLUMNY * (z tymi samymi wartosciami)? :
 -Brak zduplikowanych kolumn
  • Czy w tabeli danych znajdują się zduplikowane-powielone * WIERSZE * (z tymi samymi wartosciami)? :
 -Zduplikowane wiersze: 9

Rozmiar tabeli z duplikatami: (134, 15)

id. kod_miasta PREFEROWANY TYP SKLEPU preferowana marka sklepu preferowanay towar czynnik zakupowy płeć wykształcenie rodzaj promocji wiek liczba osób w rodzinie Miasto Liczba ludności dochody wydatki
57 R_057 M_054 osiedlowy LIDL produkty piekarnicze cena k średnie gazetka 69.0 2.0 Ostrów Wielkopolski 67825 41000.0 9948.0
58 R_057 M_054 osiedlowy LIDL produkty piekarnicze cena k średnie gazetka 69.0 2.0 Ostrów Wielkopolski 67825 41000.0 9948.0
59 R_057 M_054 osiedlowy LIDL produkty piekarnicze cena k średnie gazetka 69.0 2.0 Ostrów Wielkopolski 67825 41000.0 9948.0
61 R_058 M_052 supermarket ŻABKA produkty mleczne marka k średnie gazetka 47.0 4.0 Inowrocław 75535 41000.0 9948.0
62 R_058 M_052 supermarket ŻABKA produkty mleczne marka k średnie gazetka 47.0 4.0 Inowrocław 75535 41000.0 9948.0
63 R_058 M_052 supermarket ŻABKA produkty mleczne marka k średnie gazetka 47.0 4.0 Inowrocław 75535 41000.0 9948.0
65 R_059 M_016 supermarket ALDI mięso i wędliny cena k średnie sugestia kasjera 26.0 5.0 Toruń 202386 41000.0 9948.0
66 R_059 M_016 supermarket ALDI mięso i wędliny cena k średnie sugestia kasjera 26.0 5.0 Toruń 202386 41000.0 9948.0
67 R_059 M_016 supermarket ALDI mięso i wędliny cena k średnie sugestia kasjera 26.0 5.0 Toruń 202386 41000.0 9948.0
  • Rozwiązaniem jest dokonanie zidentyfokowanie dublujących się danych , a następnie ich usunięcie
UWAGA: Usunięto wiersze (3) z duplikującymi się danymi

Rozmiar tabeli po usunięciu duplikatów: (125, 15)

3.7. Brakujące dane

Sprawdzenie czy w danych wystepują braki (Null, None) [razem, w wierszach , w kolummnach]

Code
import pandas as pd
import altair as alt
from IPython.display import display, Markdown


def braki_sprawdzenie(dane):
    """
    Analizuje brakujące dane w tabeli.

    Parametry:
        dane (DataFrame): Ramka danych do analizy.

    Zwraca:
        None
    """
    liczba = dane.isnull().sum().sum()
    proc = (liczba / (dane.shape[0] * dane.shape[1]) * 100).round(2)

    display(Markdown('**Analiza brakujących danych:**'))
    display(Markdown('=' * 45))

    if liczba == 0:
        display(Markdown('W tabeli nie stwierdzono brakujących danych!'))
    else:
        display(Markdown(f'Liczba brakujących danych w tabeli: {liczba}'))
        display(Markdown(f'Procent brakujących danych w tabeli: {proc}%'))

        rows_with_missing_data = dane[dane.isnull().any(axis=1)]
        brakujace_dane = rows_with_missing_data.isnull().sum(axis=0)
        udzial_brakujacych_danych = ((rows_with_missing_data.isnull().sum(axis=0) / dane.shape[0]) * 100).round(1)
        wyniki = pd.DataFrame({'liczba': brakujace_dane, 'proc': udzial_brakujacych_danych})
        display(Markdown('**Brakujące dane w zmiennych (kolumny):**'))
        display(wyniki)

        brakujace_dane = rows_with_missing_data.isnull().sum(axis=1)
        udzial_brakujacych_danych = (rows_with_missing_data.isnull().sum(axis=1) / dane.shape[1] * 100).round(1)
        wyniki = pd.DataFrame({'liczba': brakujace_dane, 'proc': udzial_brakujacych_danych})
        display(Markdown('**Brakujące dane w obserwacjach (wiersze):**'))
        display(wyniki)

        # Tworzenie wykresu Altair dla brakujących danych
        dane_altair = dane.isnull().reset_index().melt(id_vars='index')
        dane_altair.columns = ['Wiersz', 'Kolumna', 'Brak']

        heatmap = alt.Chart(dane_altair).mark_rect().encode(
            x=alt.X('Kolumna:N', title='Kolumna'),
            y=alt.Y('Wiersz:O', title='Wiersz'),
            color=alt.condition(
                alt.datum.Brak == True,
                alt.value('red'),
                alt.value('lightgrey')
            ),
            tooltip=['Wiersz', 'Kolumna', 'Brak']
        ).properties(
            width=600,
            height=400,
            title='Mapa brakujących danych'
        )

        display(heatmap)

        display(Markdown('**Tabela z brakującymi danymi:**'))
        display(rows_with_missing_data)

# Przykład użycia
braki_sprawdzenie(baza)





baza['dochody'].fillna(baza['dochody'].mean().round(1), inplace=True)
baza['wydatki'].fillna(baza['wydatki'].mean().round(1), inplace=True)
baza['wiek'].fillna(baza['wiek'].mode()[0], inplace=True)
baza['liczba osób w rodzinie'].fillna(baza['liczba osób w rodzinie'].mode()[0], inplace=True)
baza['PREFEROWANY TYP SKLEPU'].fillna(baza['PREFEROWANY TYP SKLEPU'].mode()[0], inplace=True)
baza['preferowana marka sklepu'].fillna(baza['preferowana marka sklepu'].mode()[0], inplace=True)
baza['wykształcenie'].fillna(baza['wykształcenie'].mode()[0], inplace=True)

Analiza brakujących danych:

=============================================

Liczba brakujących danych w tabeli: 18

Procent brakujących danych w tabeli: 0.96%

Brakujące dane w zmiennych (kolumny):

liczba proc
id. 0 0.0
kod_miasta 0 0.0
PREFEROWANY TYP SKLEPU 2 1.6
preferowana marka sklepu 2 1.6
preferowanay towar 0 0.0
czynnik zakupowy 0 0.0
płeć 0 0.0
wykształcenie 0 0.0
rodzaj promocji 0 0.0
wiek 1 0.8
liczba osób w rodzinie 1 0.8
Miasto 0 0.0
Liczba ludności 0 0.0
dochody 5 4.0
wydatki 7 5.6

Brakujące dane w obserwacjach (wiersze):

liczba proc
1 2 13.3
2 1 6.7
5 1 6.7
6 1 6.7
13 1 6.7
14 1 6.7
18 1 6.7
39 1 6.7
86 1 6.7
112 1 6.7
120 2 13.3
125 1 6.7
126 3 20.0
129 1 6.7

Tabela z brakującymi danymi:

id. kod_miasta PREFEROWANY TYP SKLEPU preferowana marka sklepu preferowanay towar czynnik zakupowy płeć wykształcenie rodzaj promocji wiek liczba osób w rodzinie Miasto Liczba ludności dochody wydatki
1 R_002 M_050 bazarek NETTO owoce i warzywa dostępność m podstawowe gazetka 16.0 1.0 Suwałki 69442 NaN NaN
2 R_003 M_069 bazarek BIEDRONKA produkty piekarnicze jakość m podstawowe reklama rtv 49.0 1.0 Świdnica 60281 50000.0 NaN
5 R_006 M_001 bazarek DINO owoce i warzywa jakość m zawodowe reklama rtv 28.0 1.0 Warszawa 1790658 NaN 6672.0
6 R_007 M_021 bazarek LIDL produkty zbożowe marka m zawodowe gazetka NaN 5.0 Olsztyn 170904 29000.0 6756.0
13 R_014 M_043 bazarek KAUFLAND produkty mleczne cena m zawodowe gazetka 69.0 1.0 Nowy Sącz 84270 32000.0 NaN
14 R_015 M_032 bazarek ALDI produkty piekarnicze skład m średnie reklama rtv 63.0 4.0 Zielona Góra 118454 54000.0 NaN
18 R_019 M_050 supermarket BIEDRONKA owoce i warzywa cena m średnie karta 74.0 5.0 Suwałki 69442 NaN 8028.0
39 R_040 M_047 osiedlowy BIEDRONKA napoje marka m wyższe reklama rtv 69.0 NaN Piotrków Trybunalski 76279 38000.0 9144.0
86 R_078 M_049 supermarket NETTO słodycze jakość k wyższe nie korzystam 25.0 2.0 Radomsko 49898 NaN 10692.0
112 R_104 M_043 galeria DINO owoce i warzywa opakowanie k wyższe reklama rtv 77.0 5.0 Nowy Sącz 84270 NaN 12024.0
120 R_112 M_029 NaN NaN słodycze jakość k wyższe nan 53.0 5.0 Elbląg 119144 54000.0 12936.0
125 R_117 M_007 galeria BIEDRONKA mięso i wędliny jakość k wyższe nie korzystam 41.0 2.0 Szczecin 401907 57000.0 NaN
126 R_118 M_066 NaN NaN produkty zbożowe jakość k wyższe nan 63.0 2.0 Ełk 60252 57000.0 NaN
129 R_121 M_003 galeria BIEDRONKA produkty mleczne marka k wyższe nie korzystam 70.0 3.0 Łódź 684113 57000.0 NaN

W danych znajdują się barkujące dane.Z uwagi na niewielką ilość pozyskanych danych usuwanie takich danych znacznie zmniejszyło by liczbę dostępych danych, dlatego zostanie dokonana imputacja. Natomiast w przypadku wierszy i kolumn , w których znajduje się = >20 % braków o sustaną usuniete, ponieważ tak znaczna ilość braków może nie być losowa, a imputacja mogła by bardzo zniekształcić oryginalny rozkład.

Brakujące dane zostały uzupełnione za pomocą następujących metod:

  • Dochody: Brakujące wartości zostały zastąpione średnią wartością z kolumny dochody.
  • Wydatki: Brakujące wartości zostały zastąpione średnią wartością z kolumny wydatki.
  • Wiek: Brakujące wartości zostały zastąpione najczęściej występującą wartością (modą) z kolumny wiek.
  • Liczba osób w rodzinie: Brakujące wartości zostały zastąpione najczęściej występującą wartością (modą) z kolumny liczba osób w rodzinie.
  • Preferowany typ sklepu: Brakujące wartości zostały zastąpione najczęściej występującą wartością (modą) z kolumny PREFEROWANY TYP SKLEPU.
  • Preferowana marka sklepu: Brakujące wartości zostały zastąpione najczęściej występującą wartością (modą) z kolumny preferowana marka sklepu.
  • Wykształcenie: Brakujące wartości zostały zastąpione najczęściej występującą wartością (modą) z kolumny wykształcenie.
Code

display(Markdown('Ponowne sprawdzenie braków danych '))
braki_sprawdzenie(baza)

Ponowne sprawdzenie braków danych

Analiza brakujących danych:

=============================================

W tabeli nie stwierdzono brakujących danych!

3.8. Wyświetlenie uporządkowanej i wyczyszczonej tabeli

Code
display(baza)
id. kod_miasta PREFEROWANY TYP SKLEPU preferowana marka sklepu preferowanay towar czynnik zakupowy płeć wykształcenie rodzaj promocji wiek liczba osób w rodzinie Miasto Liczba ludności dochody wydatki
0 R_001 M_058 bazarek POLOMARKET napoje cena m podstawowe sugestia kasjera 30.0 2.0 Głogów 68170 54000.0 9000.0
1 R_002 M_050 bazarek NETTO owoce i warzywa dostępność m podstawowe gazetka 16.0 1.0 Suwałki 69442 43333.3 10221.5
2 R_003 M_069 bazarek BIEDRONKA produkty piekarnicze jakość m podstawowe reklama rtv 49.0 1.0 Świdnica 60281 50000.0 10221.5
3 R_004 M_059 osiedlowy LIDL produkty piekarnicze jakość m podstawowe gazetka 67.0 1.0 Stargard 71464 45000.0 6084.0
4 R_005 M_057 osiedlowy LIDL mięso i wędliny jakość m zawodowe gazetka 38.0 5.0 Ostrowiec Świętokrzyski 69715 28000.0 9000.0
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
129 R_121 M_003 galeria BIEDRONKA produkty mleczne marka k wyższe nie korzystam 70.0 3.0 Łódź 684113 57000.0 10221.5
130 R_122 M_018 galeria BIEDRONKA produkty zbożowe jakość k wyższe reklama rtv 56.0 5.0 Rzeszów 194450 54000.0 12492.0
131 R_123 M_044 galeria KAUFLAND słodycze jakość k wyższe reklama rtv 18.0 4.0 Jelenia Góra 81542 54000.0 12732.0
132 R_124 M_035 galeria BIEDRONKA produkty zbożowe marka k wyższe reklama rtv 28.0 4.0 Chorzów 110428 54000.0 12492.0
133 R_125 M_059 galeria KAUFLAND produkty zbożowe skład k wyższe reklama rtv 29.0 2.0 Stargard 71464 54000.0 12732.0

125 rows × 15 columns

3.9. Szybkie statystyki

Code
baza.describe(include='all')
baza.describe(include='all').round(2)
id. kod_miasta PREFEROWANY TYP SKLEPU preferowana marka sklepu preferowanay towar czynnik zakupowy płeć wykształcenie rodzaj promocji wiek liczba osób w rodzinie Miasto Liczba ludności dochody wydatki
count 125 125 125 125 125 125 125 125 125 125.00 125.00 125 125.00 125.00 125.00
unique 125 60 4 14 8 9 2 4 9 NaN NaN 60 NaN NaN NaN
top R_125 M_041 supermarket BIEDRONKA produkty piekarnicze jakość k wyższe gazetka NaN NaN Słupsk NaN NaN NaN
freq 1 6 38 33 23 47 85 73 45 NaN NaN 6 NaN NaN NaN
mean NaN NaN NaN NaN NaN NaN NaN NaN NaN 47.14 3.12 NaN 197365.68 43333.33 10221.46
std NaN NaN NaN NaN NaN NaN NaN NaN NaN 18.00 1.49 NaN 317857.41 7803.36 1681.83
min NaN NaN NaN NaN NaN NaN NaN NaN NaN 16.00 1.00 NaN 41575.00 28000.00 6084.00
25% NaN NaN NaN NaN NaN NaN NaN NaN NaN 30.00 2.00 NaN 69442.00 38000.00 9036.00
50% NaN NaN NaN NaN NaN NaN NaN NaN NaN 46.00 3.00 NaN 93460.00 43000.00 10221.50
75% NaN NaN NaN NaN NaN NaN NaN NaN NaN 63.00 5.00 NaN 195563.00 48000.00 11340.00
max NaN NaN NaN NaN NaN NaN NaN NaN NaN 78.00 5.00 NaN 1790658.00 61000.00 14172.00

II Podsumowanie

W niniejszym dokumencie przedstawiono kompleksowy proces przetwarzania, czyszczenia oraz analizy danych ankietowych dotyczących postaw i zachowań konsumentów podczas zakupów produktów żywnościowych. Proces ten obejmował kilka kluczowych etapów, które zostały szczegółowo opisane poniżej:

1. Pozyskanie i załadowanie danych

Dane zostały pozyskane z przeprowadzonego badania ankietowego, które dotyczyło postaw i zachowań respondentów w trakcie dokonywania zakupów produktów żywnościowych. Dane ankietowe zostały zebrane w formie plików ankieta_01a.xlsx, ankieta_01b.xlsx, ankieta_02.csv oraz miasta.json, które następnie załadowano do odpowiednich DataFrame’ów.

2. Uporządkowanie danych

W ramach tego etapu zidentyfikowano i rozwiązano następujące nieprawidłowości: - Podzielone obserwacje tych samych zmiennych pomiędzy wiele tabel. - Przechowywanie w różnych wierszach jednej kolumny wartości wielu zmiennych. - Używanie jako nagłówków kolumn wartości zamiast nazw zmiennych. - Podzielone wartości jednej zmiennej pomiędzy kilka kolumn. - Przechowywanie w jednej kolumnie połączonych wartości wielu zmiennych. - Zebrane dane podzielone pomiędzy wiele plików. - Zebrane dane w plikach zapisane w różnych formatach.

3. Czyszczenie danych

Proces czyszczenia danych obejmował: - Ujednolicenie wielkości liter w nazwach kolumn oraz wartości tekstowych. - Usunięcie znaków nie będących literami oraz niedozwolonych znaków alfanumerycznych. - Poprawę błędów literowych, ortograficznych oraz różnej pisowni. - Konwersję wartości liczbowych na odpowiednie typy danych oraz usunięcie niedozwolonych znaków. - Identyfikację i obsługę wartości odstających oraz nietypowych. - Usunięcie duplikatów oraz obsługę brakujących danych poprzez imputację.

Code


# # Tworzenie kategorii wieku
# bins = [14, 23, 40, 65, 105]
# labels = ['14-22', '23-39', '40-65', '> 66']
# baza['wiek_kat'] = pd.cut(baza['wiek'], bins=bins, labels=labels)

# # Tworzenie kategorii dochodów
# quantiles = [0, 0.25, 0.5, 0.75, 1]
# labels = ['niski', 'średni', 'wysoki', 'bardzo wysoki']
# baza['dochody_kat'] = pd.qcut(baza['dochody'], q=quantiles, labels=labels)

# # Tworzenie kategorii wydatków
# baza['wydatki_kat'] = pd.qcut(baza['wydatki'], q=quantiles, labels=labels)

# # Tworzenie kategorii wielkości miasta
# bins = [0, 50000, 200000, 500000, 5000000]
# labels = ['<50 tys.', '51 tys.-200 tys.', '201 tys.-500 tys.', 'pow 500 tys.']
# baza['wielkość miasta'] = pd.cut(baza['liczba ludności'], bins=bins, labels=labels)




# #### 3.4.3. Sprawdzenie czy w zmiennych kategorialnych wystepują obserwacje odstające ( naturalne {obserwacje rzadkie, wysoka kardynalnosć}, błędy)





# #### 3.4.4. Obsługa obserwacji odstających *(usuwanie, zamiana na NaN, utworzenie nowej kategorii, przypisanie do nowych kategorii zgodnie wiedzą dziedzinową)*

# # preferowana marka sklepu  # rzadkie i wysoka kardynalnosć - zredukować liczbę poziomów  utworzyć osobną grupę "inne" z poziomami poniżej 5


# dict_preferowana_marka_sklepu ={'Aldi':'Inna_lokalna','Dino':'Inna_lokalna',
# 'Carrefour':'Inna_lokalna',
# 'Polomarket':'Inna_lokalna',
# 'Społem':'Inna_lokalna',
# 'Intermache':'Inna_lokalna',
# 'Groszek':'Inna_lokalna',
# 'Jeżyk':'Inna_lokalna',
# 'Lewiatan':'Inna_lokalna'}

# baza['preferowana marka sklepu'] = baza['preferowana marka sklepu'].replace(dict_preferowana_marka_sklepu)




# dict_rodzaj_promocji ={
#     'inne':'inne',
#     'e-mail ':'inne',
#    'sms ':'inne',
#     'aplikacja ' :'inne',
#     'nan ':'inne',
#     'sugestia kasjera':'inne'
# }
# baza['rodzaj promocji'] = baza['rodzaj promocji'].replace(dict_rodzaj_promocji)



# czynnik_dict = {'dostępność':'inne',
#  'skład':'inne',
#  'lokalność':'inne',
#  'opinie':'inne',
#  'opakowanie':'inne',
#  'preferencje kulinarne':'inne'}

# baza['czynnik zakupowy'] = baza['czynnik zakupowy'].replace(czynnik_dict)




# zmienna_tekstowa= [
#  'preferowany typ sklepu','preferowana marka sklepu', 'preferowanay towar', 'czynnik zakupowy',
#  'rodzaj promocji']

# for element in zmienna_tekstowa:
#     display(baza[element].value_counts(normalize=True)*100)